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 "qgeoserviceprovider.h"
38 #include "qgeoserviceprovider_p.h"
39 #include "qgeoserviceproviderfactory.h"
40 
41 #include "qgeocodingmanager.h"
42 #include "qgeomappingmanager_p.h"
43 #include "qgeoroutingmanager.h"
44 #include "qplacemanager.h"
45 #include "qnavigationmanager_p.h"
46 #include "qgeocodingmanagerengine.h"
47 #include "qgeomappingmanagerengine_p.h"
48 #include "qgeoroutingmanagerengine.h"
49 #include "qplacemanagerengine.h"
50 #include "qplacemanagerengine_p.h"
51 #include "qnavigationmanagerengine_p.h"
52 
53 #include <QList>
54 #include <QString>
55 #include <QVariant>
56 
57 #include <QDebug>
58 #include <QStringList>
59 #include <QCoreApplication>
60 #include <QObject>
61 #include <QMetaObject>
62 #include <QMetaEnum>
63 #include <QtCore/private/qfactoryloader_p.h>
64 
65 QT_BEGIN_NAMESPACE
66 
67 Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader,
68         ("org.qt-project.qt.geoservice.serviceproviderfactory/5.0",
69          QLatin1String("/geoservices")))
70 
71 /*!
72     \class QGeoServiceProvider
73     \inmodule QtLocation
74     \ingroup QtLocation-common
75     \since 5.6
76 
77     \brief The QGeoServiceProvider class aggregates access to services which provide
78     geographical information.
79 
80     The Maps and Navigation API allows people to access various kinds of
81     geographical information, including functionality to perform geocoding,
82     routing and the display of maps.  The QGeoServiceProvider aggregates the
83     access to a set of these services that are provided by a single vendor.
84 
85     It is possible to mix and match service providers for the various domains,
86     so that a geocoding manager from one service provider can be used with
87     a geographic routing manager from another service provider.
88 
89     This is not recommended unless the client is able to verify that the
90     data provided by the different services are compatible, as differences
91     in the underlying data sets could cause serious incongruences between
92     the services.
93 
94     Subclasses of QGeoServiceProvider guarantee that the different services
95     that they provide are interoperable.
96 
97     At this point there are two GeoServices plugins packaged with Qt. They are
98     accessible using their provider names:
99 
100     \list
101         \li "mapbox" -> \l {Qt Location Mapbox Plugin}{Mapbox service}
102         \li "here" -> \l {Qt Location HERE Plugin}{HERE Services}
103         \li "osm" -> \l {Qt Location Open Street Map Plugin}{OpenStreetMap Services}
104         \li "esri" -> \l {Qt Location Esri Plugin}{ESRI Services}
105     \endlist
106 
107     Each service provider must follow a naming convention for their service specific
108     parameter names/keys. They use the provider name as prefix for all their
109     parameter names. For example, the \l {Qt Location HERE Plugin}{HERE} service provider
110     requires the \c here.app_id parameter. When a provider is loaded only those parameters are
111     passed on whose parameter names start with the provider name. This avoids the sharing
112     sensitive parameters such as confidential \c token or \c app_id parameters with other
113     plugins.
114 
115     Please check the GeoServices plugin specific documentation to
116     obtain a complete list of the available parameter names/keys and values.
117 */
118 
119 /*!
120     \enum QGeoServiceProvider::Error
121 
122     Describes an error related to the loading and setup of a service provider plugin.
123 
124     \value NoError
125         No error has occurred.
126     \value NotSupportedError
127         The plugin does not support this functionality.
128     \value UnknownParameterError
129         The plugin did not recognize one of the parameters it was given.
130     \value MissingRequiredParameterError
131         The plugin did not find one of the parameters it was expecting.
132     \value ConnectionError
133         The plugin could not connect to its backend service or database.
134     \value LoaderError
135         The plugin failed to load.
136 */
137 
138 /*!
139     \enum QGeoServiceProvider::RoutingFeature
140 
141     Describes the routing features supported by the geo service provider.
142 
143     \value NoRoutingFeatures            No routing features are supported.
144     \value OnlineRoutingFeature         Online routing is supported.
145     \value OfflineRoutingFeature        Offline routing is supported.
146     \value LocalizedRoutingFeature      Supports returning routes with localized addresses and
147                                         instructions.
148     \value RouteUpdatesFeature          Updating an existing route based on the current position is
149                                         supported.
150     \value AlternativeRoutesFeature     Supports returning alternative routes.
151     \value ExcludeAreasRoutingFeature   Supports specifying a areas which the returned route must
152                                         not cross.
153     \value AnyRoutingFeatures           Matches a geo service provider that provides any routing
154                                         features.
155 */
156 
157 /*!
158     \enum QGeoServiceProvider::GeocodingFeature
159 
160     Describes the geocoding features supported by the geo service provider.
161 
162     \value NoGeocodingFeatures          No geocoding features are supported.
163     \value OnlineGeocodingFeature       Online geocoding is supported.
164     \value OfflineGeocodingFeature      Offline geocoding is supported.
165     \value ReverseGeocodingFeature      Reverse geocoding is supported.
166     \value LocalizedGeocodingFeature    Supports returning geocoding results with localized
167                                         addresses.
168     \value AnyGeocodingFeatures         Matches a geo service provider that provides any geocoding
169                                         features.
170 */
171 
172 /*!
173     \enum QGeoServiceProvider::MappingFeature
174 
175     Describes the mapping features supported by the geo service provider.
176 
177     \value NoMappingFeatures        No mapping features are supported.
178     \value OnlineMappingFeature     Online mapping is supported.
179     \value OfflineMappingFeature    Offline mapping is supported.
180     \value LocalizedMappingFeature  Supports returning localized map data.
181     \value AnyMappingFeatures       Matches a geo service provider that provides any mapping
182                                     features.
183 */
184 
185 /*!
186     \enum QGeoServiceProvider::PlacesFeature
187 
188     Describes the places features supported by the geo service provider.
189 
190     \value NoPlacesFeatures             No places features are supported.
191     \value OnlinePlacesFeature          Online places is supported.
192     \value OfflinePlacesFeature         Offline places is supported.
193     \value SavePlaceFeature             Saving places is supported.
194     \value RemovePlaceFeature           Removing or deleting places is supported.
195     \value SaveCategoryFeature          Saving categories is supported.
196     \value RemoveCategoryFeature        Removing or deleting categories is supported.
197     \value PlaceRecommendationsFeature  Searching for recommended places similar to another place
198                                         is supported.
199     \value SearchSuggestionsFeature     Search suggestions is supported.
200     \value LocalizedPlacesFeature       Supports returning localized place data.
201     \value NotificationsFeature         Notifications of place and category changes is supported.
202     \value PlaceMatchingFeature         Supports matching places from two different geo service
203                                         providers.
204     \value AnyPlacesFeatures            Matches a geo service provider that provides any places
205                                         features.
206 */
207 
208 /*!
209     \enum QGeoServiceProvider::NavigationFeature
210 
211     Describes the navigation features supported by the geo service provider.
212 
213     \value NoNavigationFeatures         No navigation features are supported.
214     \value OnlineNavigationFeature      Online navigation is supported.
215     \value OfflineNavigationFeature     Offline navigation is supported.
216     \value AnyNavigationFeatures        Matches a geo service provider that provides any navigation
217                                         features.
218 */
219 
220 /*!
221     Returns a list of names of the available service providers, for use with
222     the QGeoServiceProvider constructors.
223 */
availableServiceProviders()224 QStringList QGeoServiceProvider::availableServiceProviders()
225 {
226     return QGeoServiceProviderPrivate::plugins().keys();
227 }
228 
229 /*!
230     Constructs a QGeoServiceProvider whose backend has the name \a
231     providerName, using the provided \a parameters.
232 
233     If multiple plugins have the same \a providerName, the plugin with the
234     highest reported providerVersion() will be used.
235 
236     If \a allowExperimental is true then plugins marked as experimental may be used.  By default
237     experimental plugins are not considered.
238 
239     If no plugin matching \a providerName was able to be loaded then error()
240     and errorString() will provide details about why this is the case.
241 
242     \note Before the list of \a parameters is passed on to the to-be-loaded
243     provider plugin, the list is filtered to avoid the sharing of plugin specific
244     parameters with unrelated provider plugins. Plugin specific parameter
245     keys must be prefixed with the provider name (e.g. \c here.app_id).
246 */
QGeoServiceProvider(const QString & providerName,const QVariantMap & parameters,bool allowExperimental)247 QGeoServiceProvider::QGeoServiceProvider(const QString &providerName,
248                                          const QVariantMap &parameters,
249                                          bool allowExperimental)
250     : d_ptr(new QGeoServiceProviderPrivate())
251 {
252     d_ptr->experimental = allowExperimental;
253     d_ptr->parameterMap = parameters;
254     // TODO Qt 6 Remove silent nokia rename
255     if (providerName == QStringLiteral("nokia"))
256         d_ptr->providerName = QStringLiteral("here");
257     else
258         d_ptr->providerName = providerName;
259     d_ptr->loadMeta();
260 }
261 
262 /*!
263     Destroys the service provider object.
264 */
~QGeoServiceProvider()265 QGeoServiceProvider::~QGeoServiceProvider()
266 {
267     delete d_ptr;
268 }
269 
270 /* Template for the routingFeatures(), geocodingFeatures() etc methods.
271  * Ideally, the enumName would be a template parameter, but strings
272  * are not a valid const expr. :( */
273 template <class Flags>
features(const char * enumName)274 Flags QGeoServiceProviderPrivate::features(const char *enumName)
275 {
276     const QMetaObject *mo = &QGeoServiceProvider::staticMetaObject;
277     const QMetaEnum en = mo->enumerator(
278                 mo->indexOfEnumerator(enumName));
279 
280     /* We need the typename keyword here, or Flags::enum_type will be parsed
281      * as a non-type and lead to an error */
282     Flags ret = typename Flags::enum_type(0);
283     if (this->metaData.contains(QStringLiteral("Features"))
284             && this->metaData.value(QStringLiteral("Features")).isArray()) {
285         QJsonArray features = this->metaData.value(QStringLiteral("Features")).toArray();
286         foreach (const QJsonValue &v, features) {
287             int val = en.keyToValue(v.toString().toLatin1().constData());
288             if (v.isString() && val != -1) {
289                 ret |= typename Flags::enum_type(val);
290             }
291         }
292     }
293 
294     return ret;
295 }
296 
297 /*!
298     Returns the routing features supported by the geo service provider.
299 */
routingFeatures() const300 QGeoServiceProvider::RoutingFeatures QGeoServiceProvider::routingFeatures() const
301 {
302     return d_ptr->features<RoutingFeatures>("RoutingFeatures");
303 }
304 
305 /*!
306     Returns the geocoding features supported by the geo service provider.
307 */
geocodingFeatures() const308 QGeoServiceProvider::GeocodingFeatures QGeoServiceProvider::geocodingFeatures() const
309 {
310     return d_ptr->features<GeocodingFeatures>("GeocodingFeatures");
311 }
312 
313 /*!
314     Returns the mapping features supported by the geo service provider.
315 */
mappingFeatures() const316 QGeoServiceProvider::MappingFeatures QGeoServiceProvider::mappingFeatures() const
317 {
318     return d_ptr->features<MappingFeatures>("MappingFeatures");
319 }
320 
321 /*!
322     Returns the places features supported by the geo service provider.
323 */
placesFeatures() const324 QGeoServiceProvider::PlacesFeatures QGeoServiceProvider::placesFeatures() const
325 {
326     return d_ptr->features<PlacesFeatures>("PlacesFeatures");
327 }
328 
329 /*!
330     Returns the navigation features supported by the geo service provider.
331 
332     \since QtLocation 5.11
333 */
navigationFeatures() const334 QGeoServiceProvider::NavigationFeatures QGeoServiceProvider::navigationFeatures() const
335 {
336     return d_ptr->features<NavigationFeatures>("NavigationFeatures");
337 }
338 
339 /* Sadly, these are necessary to figure out which of the factory->createX
340  * methods we need to call. Ideally it would be nice to find a way to embed
341  * these into the manager() template. */
342 template <class Engine>
createEngine(QGeoServiceProviderPrivate *)343 Engine *createEngine(QGeoServiceProviderPrivate *)
344 {
345     return 0;
346 }
createEngine(QGeoServiceProviderPrivate * d_ptr)347 template <> QGeoCodingManagerEngine *createEngine<QGeoCodingManagerEngine>(QGeoServiceProviderPrivate *d_ptr)
348 {
349     return d_ptr->factory->createGeocodingManagerEngine(d_ptr->cleanedParameterMap, &(d_ptr->geocodeError), &(d_ptr->geocodeErrorString));
350 }
createEngine(QGeoServiceProviderPrivate * d_ptr)351 template <> QGeoRoutingManagerEngine *createEngine<QGeoRoutingManagerEngine>(QGeoServiceProviderPrivate *d_ptr)
352 {
353     return d_ptr->factory->createRoutingManagerEngine(d_ptr->cleanedParameterMap, &(d_ptr->routingError), &(d_ptr->routingErrorString));
354 }
createEngine(QGeoServiceProviderPrivate * d_ptr)355 template <> QGeoMappingManagerEngine *createEngine<QGeoMappingManagerEngine>(QGeoServiceProviderPrivate *d_ptr)
356 {
357     return d_ptr->factory->createMappingManagerEngine(d_ptr->cleanedParameterMap, &(d_ptr->mappingError), &(d_ptr->mappingErrorString));
358 }
createEngine(QGeoServiceProviderPrivate * d_ptr)359 template <> QPlaceManagerEngine *createEngine<QPlaceManagerEngine>(QGeoServiceProviderPrivate *d_ptr)
360 {
361     return d_ptr->factory->createPlaceManagerEngine(d_ptr->cleanedParameterMap, &(d_ptr->placeError), &(d_ptr->placeErrorString));
362 }
createEngine(QGeoServiceProviderPrivate * d_ptr)363 template <> QNavigationManagerEngine *createEngine<QNavigationManagerEngine>(QGeoServiceProviderPrivate *d_ptr)
364 {
365     if (!d_ptr->factoryV2)
366         return nullptr;
367     return d_ptr->factoryV2->createNavigationManagerEngine(d_ptr->cleanedParameterMap, &(d_ptr->navigationError), &(d_ptr->navigationErrorString));
368 }
369 
370 /* Template for generating the code for each of the geocodingManager(),
371  * mappingManager() etc methods */
372 template <class Manager, class Engine>
manager(QGeoServiceProvider::Error * _error,QString * _errorString,Manager ** _manager)373 Manager *QGeoServiceProviderPrivate::manager(QGeoServiceProvider::Error *_error,
374                                              QString *_errorString, Manager **_manager)
375 {
376     // make local references just so this method is easier to read
377     QGeoServiceProvider::Error &error = *_error;
378     QString &errorString = *_errorString;
379     Manager *&manager = *_manager;
380 
381     if (!this->factory) {
382         this->filterParameterMap();
383         this->loadPlugin(this->parameterMap);
384     }
385 
386     if (!this->factory) {
387         error = this->error;
388         errorString = this->errorString;
389         return 0;
390     }
391 
392     if (!manager) {
393         Engine *engine = createEngine<Engine>(this); // this sets the specific error variables directly,
394                                                      // from now on the local error, errorString refs should be set.
395 
396         if (engine) {
397             engine->setManagerName(
398                         this->metaData.value(QStringLiteral("Provider")).toString());
399             engine->setManagerVersion(
400                         int(this->metaData.value(QStringLiteral("Version")).toDouble()));
401             manager = new Manager(engine);
402         } else if (error == QGeoServiceProvider::NoError) {
403             error = QGeoServiceProvider::NotSupportedError;
404             errorString = QLatin1String("The service provider does not support the ");
405             errorString.append(QLatin1String(Manager::staticMetaObject.className()));
406             errorString.append(QLatin1String(" type."));
407         }
408 
409         if (error != QGeoServiceProvider::NoError) {
410             delete manager;
411             manager = 0;
412             this->error = error;
413             this->errorString = errorString;
414         }
415 
416         if (manager && this->localeSet)
417             manager->setLocale(this->locale);
418     }
419 
420     if (manager) {
421         this->error = QGeoServiceProvider::NoError;
422         this->errorString.clear();
423     }
424 
425     return manager;
426 }
427 
428 /*!
429     Returns the QGeoCodingManager made available by the service
430     provider.
431 
432     This function will return 0 if the service provider does not provide
433     any geocoding services.
434 
435     This function will attempt to construct a QGeoCodingManager instance
436     when it is called for the first time.  If the attempt is successful the
437     QGeoCodingManager will be cached, otherwise each call of this function
438     will attempt to construct a QGeoCodingManager instance until the
439     construction is successful.
440 
441     The QGeoCodingManager is owned by this QGeoServiceProvider and should not
442     be deleted separately. Users should assume that deleting the
443     QGeoServiceProvider renders the pointer returned by this method invalid.
444 
445     After this function has been called, error() and errorString() will
446     report any errors which occurred during the construction of the
447     QGeoCodingManager.
448 */
geocodingManager() const449 QGeoCodingManager *QGeoServiceProvider::geocodingManager() const
450 {
451     QGeoCodingManager *mgr = d_ptr->manager<QGeoCodingManager, QGeoCodingManagerEngine>(
452                &(d_ptr->geocodeError), &(d_ptr->geocodeErrorString),
453                &(d_ptr->geocodingManager));
454     if (!mgr)
455         qDebug() << d_ptr->error << ", " << d_ptr->errorString;
456     return mgr;
457 }
458 
459 /*!
460     Returns the QGeoMappingManager made available by the service provider.
461 
462     This function will return 0 if the service provider does not provide
463     any mapping services.
464 
465     This function will attempt to construct a QGeoMappingManager instance
466     when it is called for the first time.  If the attempt is successful the
467     QGeoMappingManager will be cached, otherwise each call of this function
468     will attempt to construct a QGeoMappingManager instance until the
469     construction is successful.
470 
471     The QGeoMappingManager is owned by this QGeoServiceProvider and should not
472     be deleted separately. Users should assume that deleting the
473     QGeoServiceProvider renders the pointer returned by this method invalid.
474 
475     After this function has been called, error() and errorString() will
476     report any errors which occurred during the construction of the
477     QGeoMappingManager.
478 
479     \internal
480 */
mappingManager() const481 QGeoMappingManager *QGeoServiceProvider::mappingManager() const
482 {
483     QGeoMappingManager *mgr = d_ptr->manager<QGeoMappingManager, QGeoMappingManagerEngine>(
484                &(d_ptr->mappingError), &(d_ptr->mappingErrorString),
485                &(d_ptr->mappingManager));
486     if (!mgr)
487         qDebug() << d_ptr->error << ", " << d_ptr->errorString;
488     return mgr;
489 }
490 
491 /*!
492     Returns the QGeoRoutingManager made available by the service provider.
493 
494     This function will return 0 if the service provider does not provide
495     any geographic routing services.
496 
497     This function will attempt to construct a QGeoRoutingManager instance
498     when it is called for the first time.  If the attempt is successful the
499     QGeoRoutingManager will be cached, otherwise each call of this function
500     will attempt to construct a QGeoRoutingManager instance until the
501     construction is successful.
502 
503     The QGeoRoutingManager is owned by this QGeoServiceProvider and should not
504     be deleted separately. Users should assume that deleting the
505     QGeoServiceProvider renders the pointer returned by this method invalid.
506 
507     After this function has been called, error() and errorString() will
508     report any errors which occurred during the construction of the
509     QGeoRoutingManager.
510 */
routingManager() const511 QGeoRoutingManager *QGeoServiceProvider::routingManager() const
512 {
513     QGeoRoutingManager *mgr = d_ptr->manager<QGeoRoutingManager, QGeoRoutingManagerEngine>(
514                &(d_ptr->routingError), &(d_ptr->routingErrorString),
515                &(d_ptr->routingManager));
516     if (!mgr)
517         qDebug() << d_ptr->error << ", " << d_ptr->errorString;
518     return mgr;
519 }
520 
521 /*!
522     Returns the QPlaceManager made available by the service provider.
523 
524     This function will attempt to construct a QPlaceManager instance
525     when it is called for the first time.  If the attempt is successful the
526     QPlaceManager will be cached, otherwise each call of this function
527     will attempt to construct a QPlace instance until the
528     construction is successful.
529 
530     The QGeoPlaceManager is owned by this QGeoServiceProvider and should not
531     be deleted separately. Users should assume that deleting the
532     QGeoServiceProvider renders the pointer returned by this method invalid.
533 
534     After this function has been called, error() and errorString() will
535     report any errors which occurred during the construction of the QPlaceManager.
536 */
placeManager() const537 QPlaceManager *QGeoServiceProvider::placeManager() const
538 {
539     QPlaceManager *mgr = d_ptr->manager<QPlaceManager, QPlaceManagerEngine>(
540                &(d_ptr->placeError), &(d_ptr->placeErrorString),
541                 &(d_ptr->placeManager));
542     if (!mgr)
543         qDebug() << d_ptr->error << ", " << d_ptr->errorString;
544     return mgr;
545 }
546 
547 /*!
548     Returns a new QNavigationManager made available by the service provider.
549 
550     After this function has been called, error() and errorString() will
551     report any errors which occurred during the construction of the QNavigationManagerEngine.
552 */
navigationManager() const553 QNavigationManager *QGeoServiceProvider::navigationManager() const
554 {
555     QNavigationManager * mgr = d_ptr->manager<QNavigationManager, QNavigationManagerEngine>(
556                &(d_ptr->navigationError), &(d_ptr->navigationErrorString),
557                 &(d_ptr->navigationManager));
558     if (!mgr)
559         qDebug() << d_ptr->error << ", " << d_ptr->errorString;
560     return mgr;
561 }
562 
563 /*!
564     Returns an error code describing the error which occurred during the
565     last operation that was performed by this class.
566 */
error() const567 QGeoServiceProvider::Error QGeoServiceProvider::error() const
568 {
569     return d_ptr->error;
570 }
571 
572 /*!
573     Returns a string describing the error which occurred during the
574     last operation that was performed by this class.
575 */
errorString() const576 QString QGeoServiceProvider::errorString() const
577 {
578     return d_ptr->errorString;
579 }
580 
581 /*!
582     Returns an error code describing the error which occurred during the
583     last attempt to create a mapping manager.
584 
585     \since 5.13
586 */
mappingError() const587 QGeoServiceProvider::Error QGeoServiceProvider::mappingError() const
588 {
589     return d_ptr->mappingError;
590 }
591 
592 /*!
593     Returns a string describing the error which occurred during the
594     last attempt to create a mapping manager.
595 
596     \since 5.13
597 */
mappingErrorString() const598 QString QGeoServiceProvider::mappingErrorString() const
599 {
600     return d_ptr->mappingErrorString;
601 }
602 
603 /*!
604     Returns an error code describing the error which occurred during the
605     last attempt to create a geocoding manager.
606 
607     \since 5.13
608 */
geocodingError() const609 QGeoServiceProvider::Error QGeoServiceProvider::geocodingError() const
610 {
611     return d_ptr->geocodeError;
612 }
613 
614 /*!
615     Returns a string describing the error which occurred during the
616     last attempt to create a geocoding manager.
617 
618     \since 5.13
619 */
geocodingErrorString() const620 QString QGeoServiceProvider::geocodingErrorString() const
621 {
622     return d_ptr->geocodeErrorString;
623 }
624 
625 /*!
626     Returns an error code describing the error which occurred during the
627     last attempt to create a routing manager.
628 
629     \since 5.13
630 */
routingError() const631 QGeoServiceProvider::Error QGeoServiceProvider::routingError() const
632 {
633     return d_ptr->routingError;
634 }
635 
636 /*!
637     Returns a string describing the error which occurred during the
638     last attempt to create a routing manager.
639 
640     \since 5.13
641 */
routingErrorString() const642 QString QGeoServiceProvider::routingErrorString() const
643 {
644     return d_ptr->routingErrorString;
645 }
646 
647 /*!
648     Returns an error code describing the error which occurred during the
649     last attempt to create a places manager.
650 
651     \since 5.13
652 */
placesError() const653 QGeoServiceProvider::Error QGeoServiceProvider::placesError() const
654 {
655     return d_ptr->placeError;
656 }
657 
658 /*!
659     Returns a string describing the error which occurred during the
660     last attempt to create a places manager.
661 
662     \since 5.13
663 */
placesErrorString() const664 QString QGeoServiceProvider::placesErrorString() const
665 {
666     return d_ptr->placeErrorString;
667 }
668 
669 /*!
670     Returns an error code describing the error which occurred during the
671     last attempt to create a navigation manager.
672 
673     \since 5.13
674 */
navigationError() const675 QGeoServiceProvider::Error QGeoServiceProvider::navigationError() const
676 {
677     return d_ptr->navigationError;
678 }
679 
680 /*!
681     Returns a string describing the error which occurred during the
682     last attempt to create a navigation manager.
683 
684     \since 5.13
685 */
navigationErrorString() const686 QString QGeoServiceProvider::navigationErrorString() const
687 {
688     return d_ptr->navigationErrorString;
689 }
690 
691 /*!
692     Sets whether experimental plugins are considered when locating the
693     correct plugin library for this service provider to \a allow.
694 
695     \b {Important:} this will destroy any existing managers held by this
696     service provider instance. You should be sure not to attempt to use any
697     pointers that you have previously retrieved after calling this method.
698 */
setAllowExperimental(bool allow)699 void QGeoServiceProvider::setAllowExperimental(bool allow)
700 {
701     d_ptr->experimental = allow;
702     d_ptr->unload();
703     d_ptr->loadMeta();
704 }
705 
setQmlEngine(QQmlEngine * engine)706 void QGeoServiceProvider::setQmlEngine(QQmlEngine *engine)
707 {
708     d_ptr->qmlEngine = engine;
709 }
710 
711 /*!
712     Sets the parameters used to construct individual manager classes for
713     this service provider to \a parameters.
714 
715     Before the list of \a parameters is passed on to the to-be-loaded
716     service provider, the list is filtered to avoid the sharing of provider specific
717     parameters with unrelated service providers. Provider specific parameter
718     keys must be prefixed with the provider name (e.g. \c here.app_id).
719 
720     \b {Important:} this will destroy any existing managers held by this
721     service provider instance. You should be sure not to attempt to use any
722     pointers that you have previously retrieved after calling this method.
723 */
setParameters(const QVariantMap & parameters)724 void QGeoServiceProvider::setParameters(const QVariantMap &parameters)
725 {
726     d_ptr->parameterMap = parameters;
727     d_ptr->unload();
728     d_ptr->loadMeta();
729 }
730 
731 /*!
732     Sets the locale used by this service provider to \a locale. If the relevant features
733     (see LocalizedMappingFeature etc), this will change the languages, units
734     and other locale-specific attributes of the provider's data.
735 */
setLocale(const QLocale & locale)736 void QGeoServiceProvider::setLocale(const QLocale &locale)
737 {
738     d_ptr->locale = locale;
739     d_ptr->localeSet = true;
740 
741     if (d_ptr->geocodingManager)
742         d_ptr->geocodingManager->setLocale(locale);
743     if (d_ptr->routingManager)
744         d_ptr->routingManager->setLocale(locale);
745     if (d_ptr->mappingManager)
746         d_ptr->mappingManager->setLocale(locale);
747     if (d_ptr->placeManager)
748         d_ptr->placeManager->setLocale(locale);
749     if (d_ptr->navigationManager)
750         d_ptr->navigationManager->setLocale(locale);
751 }
752 
753 /*******************************************************************************
754 *******************************************************************************/
755 
QGeoServiceProviderPrivate()756 QGeoServiceProviderPrivate::QGeoServiceProviderPrivate()
757     : factory(0),
758       experimental(false),
759       geocodingManager(0),
760       routingManager(0),
761       mappingManager(0),
762       placeManager(0),
763       geocodeError(QGeoServiceProvider::NoError),
764       routingError(QGeoServiceProvider::NoError),
765       mappingError(QGeoServiceProvider::NoError),
766       placeError(QGeoServiceProvider::NoError),
767       error(QGeoServiceProvider::NoError),
768       localeSet(false)
769 {
770     metaData.insert(QStringLiteral("index"), -1);
771 }
772 
~QGeoServiceProviderPrivate()773 QGeoServiceProviderPrivate::~QGeoServiceProviderPrivate()
774 {
775     delete geocodingManager;
776     delete routingManager;
777     delete mappingManager;
778     delete placeManager;
779     delete navigationManager;
780 }
781 
unload()782 void QGeoServiceProviderPrivate::unload()
783 {
784     delete geocodingManager;
785     geocodingManager = 0;
786 
787     delete routingManager;
788     routingManager = 0;
789 
790     delete mappingManager;
791     mappingManager = 0;
792 
793     delete placeManager;
794     placeManager = 0;
795 
796     delete navigationManager;
797     navigationManager = nullptr;
798 
799     factory = factoryV2 = factoryV3 = nullptr;
800     error = QGeoServiceProvider::NoError;
801     errorString = QLatin1String("");
802     metaData = QJsonObject();
803     metaData.insert(QStringLiteral("index"), -1);
804 }
805 
806 /* Filter out any parameter that doesn't match any plugin */
filterParameterMap()807 void QGeoServiceProviderPrivate::filterParameterMap()
808 {
809     const auto availablePlugins = QGeoServiceProviderPrivate::plugins();
810 
811     cleanedParameterMap = parameterMap;
812     for (auto it = availablePlugins.keyBegin(), end = availablePlugins.keyEnd(); it != end; ++it) {
813         if (*it == providerName) // don't remove parameters for current provider
814             continue;
815 
816         QVariantMap::iterator i = cleanedParameterMap.begin();
817         while (i != cleanedParameterMap.end()) {
818             // remove every parameter meant for other plugins
819             if (i.key().startsWith(QString(*it + QLatin1Char('.'))))
820                 i = cleanedParameterMap.erase(i);
821             else
822                 ++i;
823         }
824     }
825 }
826 
loadMeta()827 void QGeoServiceProviderPrivate::loadMeta()
828 {
829     factory = factoryV2 = factoryV3 = nullptr;
830     metaData = QJsonObject();
831     metaData.insert(QStringLiteral("index"), -1);
832     error = QGeoServiceProvider::NotSupportedError;
833     errorString = QString(QLatin1String("The geoservices provider %1 is not supported.")).arg(providerName);
834 
835     QList<QJsonObject> candidates = QGeoServiceProviderPrivate::plugins().values(providerName);
836 
837     int versionFound = -1;
838     int idx = -1;
839 
840     // figure out which version of the plugin we want
841     // (always latest unless experimental)
842     for (int i = 0; i < candidates.size(); ++i) {
843         QJsonObject meta = candidates[i];
844         if (meta.contains(QStringLiteral("Version"))
845                 && meta.value(QStringLiteral("Version")).isDouble()
846                 && meta.contains(QStringLiteral("Experimental"))
847                 && meta.value(QStringLiteral("Experimental")).isBool()) {
848             int ver = int(meta.value(QStringLiteral("Version")).toDouble());
849             if (ver > versionFound && !(!experimental && meta.value(QStringLiteral("Experimental")).toBool())) {
850                 versionFound = ver;
851                 idx = i;
852             }
853         }
854     }
855 
856     if (idx != -1) {
857         error = QGeoServiceProvider::NoError;
858         errorString = QStringLiteral("");
859         metaData = candidates[idx];
860     }
861 }
862 
loadPlugin(const QVariantMap & parameters)863 void QGeoServiceProviderPrivate::loadPlugin(const QVariantMap &parameters)
864 {
865     Q_UNUSED(parameters);
866 
867     if (int(metaData.value(QStringLiteral("index")).toDouble()) < 0) {
868         error = QGeoServiceProvider::NotSupportedError;
869         errorString = QString(QLatin1String("The geoservices provider is not supported."));
870         factory = factoryV2 = factoryV3 = nullptr;
871         return;
872     }
873 
874     error = QGeoServiceProvider::NoError;
875     errorString = QLatin1String("");
876 
877     int idx = int(metaData.value(QStringLiteral("index")).toDouble());
878 
879     // load the actual plugin
880     QObject *instance = loader()->instance(idx);
881     if (!instance) {
882         error = QGeoServiceProvider::LoaderError;
883         errorString = QLatin1String("loader()->instance(idx) failed to return an instance. Set the environment variable QT_DEBUG_PLUGINS to see more details.");
884         return;
885     }
886     factoryV3 = qobject_cast<QGeoServiceProviderFactoryV3 *>(instance);
887     if (!factoryV3) {
888         factoryV2 = qobject_cast<QGeoServiceProviderFactoryV2 *>(instance);
889         if (!factoryV2)
890             factory = qobject_cast<QGeoServiceProviderFactory *>(instance);
891         else
892             factory = factoryV2;
893     } else {
894         factory = factoryV2 = factoryV3;
895         factoryV3->setQmlEngine(qmlEngine);
896     }
897 }
898 
plugins(bool reload)899 QMultiHash<QString, QJsonObject> QGeoServiceProviderPrivate::plugins(bool reload)
900 {
901     static QMultiHash<QString, QJsonObject> plugins;
902     static bool alreadyDiscovered = false;
903 
904     if (reload == true)
905         alreadyDiscovered = false;
906 
907     if (!alreadyDiscovered) {
908         loadPluginMetadata(plugins);
909         alreadyDiscovered = true;
910     }
911     return plugins;
912 }
913 
loadPluginMetadata(QMultiHash<QString,QJsonObject> & list)914 void QGeoServiceProviderPrivate::loadPluginMetadata(QMultiHash<QString, QJsonObject> &list)
915 {
916     QFactoryLoader *l = loader();
917     QList<QJsonObject> meta = l->metaData();
918     for (int i = 0; i < meta.size(); ++i) {
919         QJsonObject obj = meta.at(i).value(QStringLiteral("MetaData")).toObject();
920         obj.insert(QStringLiteral("index"), i);
921         list.insert(obj.value(QStringLiteral("Provider")).toString(), obj);
922     }
923 }
924 
925 
926 QT_END_NAMESPACE
927 
928