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 "qdeclarativegeoroutemodel_p.h"
38#include "qdeclarativegeoroute_p.h"
39#include "error_messages_p.h"
40#include "locationvaluetypehelper_p.h"
41
42#include <QtCore/QCoreApplication>
43#include <QtQml/QQmlEngine>
44#include <QtQml/qqmlinfo.h>
45#include <QtQml/private/qqmlengine_p.h>
46#include <QtQml/private/qv4scopedvalue_p.h>
47#include <QtQml/private/qv4arrayobject_p.h>
48#include <QtLocation/QGeoRoutingManager>
49#include <QtPositioning/QGeoRectangle>
50#include "qdeclarativegeomapparameter_p.h"
51
52QT_BEGIN_NAMESPACE
53
54static bool compareFloats(qreal a, qreal b)
55{
56 return (qIsNaN(d: a) && qIsNaN(d: b))
57 || a == b;
58}
59
60static bool compareParameterList(const QList<QDeclarativeGeoMapParameter *> &a, const QList<QDeclarativeGeoMapParameter *> &b)
61{
62 if (a.size() != b.size())
63 return false;
64 if (a != b) {
65 for (int i = 0; i < a.size(); ++i) {
66 if (! (*a.at(i) == *b.at(i)))
67 return false;
68 }
69 }
70 return true;
71}
72
73static int findWaypoint(const QList<QDeclarativeGeoWaypoint *> &waypoints, const QDeclarativeGeoWaypoint *w)
74{
75 for (int i = waypoints.size() - 1; i >= 0; --i) {
76 if (waypoints.at(i) == w || *waypoints.at(i) == *w)
77 return i;
78 }
79 return -1;
80}
81
82static int findWaypoint(const QList<QDeclarativeGeoWaypoint *> &waypoints, const QGeoCoordinate &c)
83{
84 for (int i = waypoints.size() - 1; i >= 0; --i) {
85 if (waypoints.at(i)->coordinate() == c)
86 return i;
87 }
88 return -1;
89}
90
91static QList<QGeoCoordinate> waypointCoordinates(const QList<QDeclarativeGeoWaypoint *> &waypoints)
92{
93 QList<QGeoCoordinate> res;
94 for (const QDeclarativeGeoWaypoint *w: waypoints)
95 res << w->coordinate();
96 return res;
97}
98
99static QList<QVariantMap> waypointMetadata(const QList<QDeclarativeGeoWaypoint *> &waypoints)
100{
101 QList<QVariantMap> res;
102 for (QDeclarativeGeoWaypoint *w: waypoints)
103 res << w->metadata();
104 return res;
105}
106
107/*!
108 \qmltype RouteModel
109 \instantiates QDeclarativeGeoRouteModel
110 \inqmlmodule QtLocation
111 \ingroup qml-QtLocation5-routing
112 \since QtLocation 5.5
113
114 \brief The RouteModel type provides access to routes.
115
116 The RouteModel type is used as part of a model/view grouping to retrieve
117 geographic routes from a backend provider. Routes include data about driving
118 directions between two points, walking directions with multiple waypoints,
119 and various other similar concepts. It functions much like other Model
120 types in QML (see for example \l {Models and Views in Qt Quick#Models}{ListModel}
121 and \l {QtQuick.XmlListModel::XmlListModel}{XmlListModel}), and interacts with
122 views such as \l MapItemView, and \l{ListView}.
123
124 Like \l Map and \l GeocodeModel, all the data for a RouteModel to work comes
125 from a services plugin. This is contained in the \l{plugin} property, and
126 this must be set before the RouteModel can do any useful work.
127
128 Once the plugin is set, create a \l RouteQuery with the appropriate
129 waypoints and other settings, and set the RouteModel's \l{query}
130 property. If \l autoUpdate is enabled, the update will being automatically.
131 Otherwise, the \l{update} method may be used. By default, autoUpdate is
132 disabled.
133
134 The data stored and returned in the RouteModel consists of \l Route objects,
135 as a list with the role name "routeData". See the documentation for \l Route
136 for further details on its structure and contents.
137
138 \section2 Example Usage
139
140 The following snippet is two-part, showing firstly the declaration of
141 objects, and secondly a short piece of procedural code using it. We set
142 the routeModel's \l{autoUpdate} property to false, and call \l{update} once
143 the query is set up, to avoid useless extra requests halfway through the
144 set up of the query.
145
146 \code
147 Plugin {
148 id: aPlugin
149 name: "osm"
150 }
151
152 RouteQuery {
153 id: aQuery
154 }
155
156 RouteModel {
157 id: routeModel
158 plugin: aPlugin
159 query: aQuery
160 autoUpdate: false
161 }
162 \endcode
163
164 \code
165 {
166 aQuery.addWaypoint(...)
167 aQuery.addWaypoint(...)
168 aQuery.travelModes = ...
169 routeModel.update()
170 }
171 \endcode
172
173*/
174
175QDeclarativeGeoRouteModel::QDeclarativeGeoRouteModel(QObject *parent)
176 : QAbstractListModel(parent),
177 complete_(false),
178 plugin_(0),
179 routeQuery_(0),
180 autoUpdate_(false),
181 status_(QDeclarativeGeoRouteModel::Null),
182 error_(QDeclarativeGeoRouteModel::NoError)
183{
184}
185
186QDeclarativeGeoRouteModel::~QDeclarativeGeoRouteModel()
187{
188 if (!routes_.empty()) {
189 qDeleteAll(c: routes_);
190 routes_.clear();
191 }
192}
193
194/*!
195 \qmlproperty int QtLocation::RouteModel::count
196
197 This property holds how many routes the model currently has.
198 Amongst other uses, you can use this value when accessing routes
199 via the QtLocation::RouteModel::get -method.
200*/
201
202int QDeclarativeGeoRouteModel::count() const
203{
204 return routes_.count();
205}
206
207/*!
208 \qmlmethod void QtLocation::RouteModel::reset()
209
210 Resets the model. All route data is cleared, any outstanding requests
211 are aborted and possible errors are cleared. Model status will be set
212 to RouteModel.Null
213*/
214
215void QDeclarativeGeoRouteModel::reset()
216{
217 if (!routes_.isEmpty()) {
218 beginResetModel();
219 qDeleteAll(c: routes_);
220 routes_.clear();
221 emit countChanged();
222 emit routesChanged();
223 endResetModel();
224 }
225
226 emit abortRequested();
227 setError(error: NoError, errorString: QString());
228 setStatus(QDeclarativeGeoRouteModel::Null);
229}
230
231/*!
232 \qmlmethod void QtLocation::RouteModel::cancel()
233
234 Cancels any outstanding requests and clears errors. Model status will be set to either
235 RouteModel.Null or RouteModel.Ready.
236*/
237void QDeclarativeGeoRouteModel::cancel()
238{
239 emit abortRequested();
240 setError(error: NoError, errorString: QString());
241 setStatus(routes_.isEmpty() ? Null : Ready);
242}
243
244/*!
245 \qmlmethod Route QtLocation::RouteModel::get(int index)
246
247 Returns the Route at the specified \a index. Use the \l count
248 property to check the amount of routes available. The routes
249 are indexed from zero, so the accessible range is 0...(count - 1).
250
251 If you access out of bounds, a zero (null object) is returned and
252 a warning is issued.
253*/
254
255QDeclarativeGeoRoute *QDeclarativeGeoRouteModel::get(int index)
256{
257 if (index < 0 || index >= routes_.count()) {
258 qmlWarning(me: this) << QStringLiteral("Index '%1' out of range").arg(a: index);
259 return 0;
260 }
261 return routes_.at(i: index);
262}
263
264/*!
265 \internal
266*/
267void QDeclarativeGeoRouteModel::componentComplete()
268{
269 complete_ = true;
270 if (autoUpdate_) {
271 update();
272 }
273}
274
275/*!
276 \internal
277*/
278int QDeclarativeGeoRouteModel::rowCount(const QModelIndex &parent) const
279{
280 Q_UNUSED(parent);
281 return routes_.count();
282}
283
284/*!
285 \internal
286*/
287QVariant QDeclarativeGeoRouteModel::data(const QModelIndex &index, int role) const
288{
289 if (!index.isValid()) {
290 qmlWarning(me: this) << QStringLiteral("Error in indexing route model's data (invalid index).");
291 return QVariant();
292 }
293
294 if (index.row() >= routes_.count()) {
295 qmlWarning(me: this) << QStringLiteral("Fatal error in indexing route model's data (index overflow).");
296 return QVariant();
297 }
298
299 if (role == RouteRole) {
300 QObject *route = routes_.at(i: index.row());
301 return QVariant::fromValue(value: route);
302 }
303 return QVariant();
304}
305
306QHash<int, QByteArray> QDeclarativeGeoRouteModel::roleNames() const
307{
308 QHash<int, QByteArray> roleNames = QAbstractListModel::roleNames();
309 roleNames.insert(akey: RouteRole, avalue: "routeData");
310 return roleNames;
311}
312
313/*!
314 \internal
315*/
316void QDeclarativeGeoRouteModel::setPlugin(QDeclarativeGeoServiceProvider *plugin)
317{
318 if (plugin_ == plugin)
319 return;
320
321 reset(); // reset the model
322
323 if (plugin_)
324 disconnect(sender: plugin_, SIGNAL(localesChanged()), receiver: this, SIGNAL(measurementSystemChanged()));
325 if (plugin)
326 connect(sender: plugin, SIGNAL(localesChanged()), receiver: this, SIGNAL(measurementSystemChanged()));
327
328 plugin_ = plugin;
329
330 if (complete_)
331 emit pluginChanged();
332
333 if (!plugin)
334 return;
335
336 if (plugin_->isAttached()) {
337 pluginReady();
338 } else {
339 connect(sender: plugin_, SIGNAL(attached()),
340 receiver: this, SLOT(pluginReady()));
341 }
342}
343
344/*!
345 \internal
346*/
347void QDeclarativeGeoRouteModel::pluginReady()
348{
349 QGeoServiceProvider *serviceProvider = plugin_->sharedGeoServiceProvider();
350 QGeoRoutingManager *routingManager = serviceProvider->routingManager();
351
352 if (serviceProvider->routingError() != QGeoServiceProvider::NoError) {
353 QDeclarativeGeoRouteModel::RouteError newError = UnknownError;
354 switch (serviceProvider->routingError()) {
355 case QGeoServiceProvider::NotSupportedError:
356 newError = EngineNotSetError; break;
357 case QGeoServiceProvider::UnknownParameterError:
358 newError = UnknownParameterError; break;
359 case QGeoServiceProvider::MissingRequiredParameterError:
360 newError = MissingRequiredParameterError; break;
361 case QGeoServiceProvider::ConnectionError:
362 newError = CommunicationError; break;
363 default:
364 break;
365 }
366
367 setError(error: newError, errorString: serviceProvider->routingErrorString());
368 return;
369 }
370
371 if (!routingManager) {
372 setError(error: EngineNotSetError, errorString: tr(s: "Plugin does not support routing."));
373 return;
374 }
375
376 connect(sender: routingManager, SIGNAL(finished(QGeoRouteReply*)),
377 receiver: this, SLOT(routingFinished(QGeoRouteReply*)));
378 connect(sender: routingManager, SIGNAL(error(QGeoRouteReply*,QGeoRouteReply::Error,QString)),
379 receiver: this, SLOT(routingError(QGeoRouteReply*,QGeoRouteReply::Error,QString)));
380}
381
382/*!
383 \internal
384*/
385void QDeclarativeGeoRouteModel::queryDetailsChanged()
386{
387 if (autoUpdate_ && complete_)
388 update();
389}
390
391/*!
392 \qmlproperty Plugin QtLocation::RouteModel::plugin
393
394 This property holds the plugin that providers the actual
395 routing service. Note that all plugins do not necessarily
396 provide routing (could for example provide only geocoding or maps).
397
398 A valid plugin must be set before the RouteModel can perform any useful
399 operations.
400
401 \sa Plugin
402*/
403
404QDeclarativeGeoServiceProvider *QDeclarativeGeoRouteModel::plugin() const
405{
406 return plugin_;
407}
408
409/*!
410 \internal
411*/
412void QDeclarativeGeoRouteModel::setQuery(QDeclarativeGeoRouteQuery *query)
413{
414 if (!query || query == routeQuery_)
415 return;
416 if (routeQuery_)
417 routeQuery_->disconnect(receiver: this);
418 routeQuery_ = query;
419 connect(sender: query, SIGNAL(queryDetailsChanged()), receiver: this, SLOT(queryDetailsChanged()));
420 if (complete_) {
421 emit queryChanged();
422 if (autoUpdate_)
423 update();
424 }
425}
426
427/*!
428 \qmlproperty RouteQuery QtLocation::RouteModel::query
429
430 This property holds the data of the route request.
431 The primary data are the waypoint coordinates and possible further
432 preferences (means of traveling, things to avoid on route etc).
433*/
434
435QDeclarativeGeoRouteQuery *QDeclarativeGeoRouteModel::query() const
436{
437 return routeQuery_;
438}
439
440/*!
441 \internal
442*/
443void QDeclarativeGeoRouteModel::setAutoUpdate(bool autoUpdate)
444{
445 if (autoUpdate_ == autoUpdate)
446 return;
447 autoUpdate_ = autoUpdate;
448 if (complete_)
449 emit autoUpdateChanged();
450}
451
452/*!
453 \qmlproperty bool QtLocation::RouteModel::autoUpdate
454
455 This property controls whether the Model automatically updates in response
456 to changes in its attached RouteQuery. The default value of this property
457 is false.
458
459 If setting this value to 'true', note that any change at all in
460 the RouteQuery object set in the \l{query} property will trigger a new
461 request to be sent. If you are adjusting many properties of the RouteQuery
462 with autoUpdate enabled, this can generate large numbers of useless (and
463 later discarded) requests.
464*/
465
466bool QDeclarativeGeoRouteModel::autoUpdate() const
467{
468 return autoUpdate_;
469}
470
471/*!
472 \qmlproperty Locale::MeasurementSystem QtLocation::RouteModel::measurementSystem
473
474 This property holds the measurement system which will be used when calculating the route. This
475 property is changed when the \l {QtLocation::Plugin::locales}{Plugin::locales} property of
476 \l {QtLocation::RouteModel::plugin}{plugin} changes.
477
478 If setting this property it must be set after the \l {QtLocation::RouteModel::plugin}{plugin}
479 property is set.
480*/
481void QDeclarativeGeoRouteModel::setMeasurementSystem(QLocale::MeasurementSystem ms)
482{
483 if (!plugin_)
484 return;
485
486 QGeoServiceProvider *serviceProvider = plugin_->sharedGeoServiceProvider();
487 if (!serviceProvider)
488 return;
489
490 QGeoRoutingManager *routingManager = serviceProvider->routingManager();
491 if (!routingManager)
492 return;
493
494 if (routingManager->measurementSystem() == ms)
495 return;
496
497 routingManager->setMeasurementSystem(ms);
498 emit measurementSystemChanged();
499}
500
501QLocale::MeasurementSystem QDeclarativeGeoRouteModel::measurementSystem() const
502{
503 if (!plugin_)
504 return QLocale().measurementSystem();
505
506 QGeoServiceProvider *serviceProvider = plugin_->sharedGeoServiceProvider();
507 if (!serviceProvider) {
508 if (plugin_->locales().isEmpty())
509 return QLocale().measurementSystem();
510
511 return QLocale(plugin_->locales().first()).measurementSystem();
512 }
513
514 QGeoRoutingManager *routingManager = serviceProvider->routingManager();
515 if (!routingManager) {
516 if (plugin_->locales().isEmpty())
517 return QLocale().measurementSystem();
518
519 return QLocale(plugin_->locales().first()).measurementSystem();
520 }
521
522 return routingManager->measurementSystem();
523}
524
525/*!
526 \internal
527*/
528void QDeclarativeGeoRouteModel::setStatus(QDeclarativeGeoRouteModel::Status status)
529{
530 if (status_ == status)
531 return;
532
533 status_ = status;
534
535 if (complete_)
536 emit statusChanged();
537}
538
539/*!
540 \qmlproperty enumeration QtLocation::RouteModel::status
541
542 This read-only property holds the current status of the model.
543
544 \list
545 \li RouteModel.Null - No route requests have been issued or \l reset has been called.
546 \li RouteModel.Ready - Route request(s) have finished successfully.
547 \li RouteModel.Loading - Route request has been issued but not yet finished
548 \li RouteModel.Error - Routing error has occurred, details are in \l error and \l errorString
549 \endlist
550*/
551
552QDeclarativeGeoRouteModel::Status QDeclarativeGeoRouteModel::status() const
553{
554 return status_;
555}
556
557/*!
558 \qmlproperty string QtLocation::RouteModel::errorString
559
560 This read-only property holds the textual presentation of the latest routing error.
561 If no error has occurred or the model has been reset, an empty string is returned.
562
563 An empty string may also be returned if an error occurred which has no associated
564 textual representation.
565*/
566
567QString QDeclarativeGeoRouteModel::errorString() const
568{
569 return errorString_;
570}
571
572/*!
573 \qmlproperty enumeration QtLocation::RouteModel::error
574
575 This read-only property holds the latest error value of the routing request.
576
577 \list
578 \li RouteModel.NoError - No error has occurred.
579 \li RouteModel.CommunicationError - An error occurred while communicating with the service provider.
580 \li RouteModel.EngineNotSetError - The model's plugin property was not set or there is no routing manager associated with the plugin.
581 \li RouteModel.MissingRequiredParameterError - A required parameter was not specified.
582 \li RouteModel.ParseError - The response from the service provider was in an unrecognizable format.
583 \li RouteModel.UnknownError - An error occurred which does not fit into any of the other categories.
584 \li RouteModel.UnknownParameterError - The plugin did not recognize one of the parameters it was given.
585 \li RouteModel.UnsupportedOptionError - The requested operation is not supported by the routing provider.
586 This may happen when the loaded engine does not support a particular
587 type of routing request.
588 \endlist
589*/
590
591QDeclarativeGeoRouteModel::RouteError QDeclarativeGeoRouteModel::error() const
592{
593 return error_;
594}
595
596void QDeclarativeGeoRouteModel::setError(RouteError error, const QString& errorString)
597{
598 if (error_ == error && errorString_ == errorString)
599 return;
600 error_ = error;
601 errorString_ = errorString;
602 emit errorChanged();
603}
604
605/*!
606 \qmlmethod void QtLocation::RouteModel::update()
607
608 Instructs the RouteModel to update its data. This is most useful
609 when \l autoUpdate is disabled, to force a refresh when the query
610 has been changed.
611*/
612void QDeclarativeGeoRouteModel::update()
613{
614 if (!complete_)
615 return;
616
617 if (!plugin_) {
618 setError(error: EngineNotSetError, errorString: tr(s: "Cannot route, plugin not set."));
619 return;
620 }
621
622 QGeoServiceProvider *serviceProvider = plugin_->sharedGeoServiceProvider();
623 if (!serviceProvider)
624 return;
625
626 QGeoRoutingManager *routingManager = serviceProvider->routingManager();
627 if (!routingManager) {
628 setError(error: EngineNotSetError, errorString: tr(s: "Cannot route, route manager not set."));
629 return;
630 }
631 if (!routeQuery_) {
632 setError(error: ParseError, errorString: tr(s: "Cannot route, valid query not set."));
633 return;
634 }
635 emit abortRequested(); // Clear previous requests
636 QGeoRouteRequest request = routeQuery_->routeRequest();
637 if (request.waypoints().count() < 2) {
638 setError(error: ParseError,errorString: tr(s: "Not enough waypoints for routing."));
639 return;
640 }
641
642 setError(error: NoError, errorString: QString());
643
644 QGeoRouteReply *reply = routingManager->calculateRoute(request);
645 setStatus(QDeclarativeGeoRouteModel::Loading);
646 if (!reply->isFinished()) {
647 connect(sender: this, signal: &QDeclarativeGeoRouteModel::abortRequested, receiver: reply, slot: &QGeoRouteReply::abort);
648 } else {
649 if (reply->error() == QGeoRouteReply::NoError) {
650 routingFinished(reply);
651 } else {
652 routingError(reply, error: reply->error(), errorString: reply->errorString());
653 }
654 }
655}
656
657/*!
658 \internal
659*/
660void QDeclarativeGeoRouteModel::routingFinished(QGeoRouteReply *reply)
661{
662 if (!reply)
663 return;
664 reply->deleteLater();
665 if (reply->error() != QGeoRouteReply::NoError)
666 return;
667
668 beginResetModel();
669 int oldCount = routes_.count();
670 qDeleteAll(c: routes_);
671 // Convert routes to declarative
672 routes_.clear();
673 for (int i = 0; i < reply->routes().size(); ++i) {
674 QDeclarativeGeoRoute *route = new QDeclarativeGeoRoute(reply->routes().at(i), this);
675 QQmlEngine::setContextForObject(route, QQmlEngine::contextForObject(this));
676 routes_.append(t: route);
677 }
678 endResetModel();
679
680 setError(error: NoError, errorString: QString());
681 setStatus(QDeclarativeGeoRouteModel::Ready);
682
683 if (oldCount != 0 || routes_.count() != 0)
684 emit routesChanged();
685 if (oldCount != routes_.count())
686 emit countChanged();
687}
688
689/*!
690 \internal
691*/
692void QDeclarativeGeoRouteModel::routingError(QGeoRouteReply *reply,
693 QGeoRouteReply::Error error,
694 const QString &errorString)
695{
696 if (!reply)
697 return;
698 reply->deleteLater();
699 setError(error: static_cast<QDeclarativeGeoRouteModel::RouteError>(error), errorString);
700 setStatus(QDeclarativeGeoRouteModel::Error);
701}
702
703
704/*!
705 \qmltype RouteQuery
706 \instantiates QDeclarativeGeoRouteQuery
707 \inqmlmodule QtLocation
708 \ingroup qml-QtLocation5-routing
709 \since QtLocation 5.5
710
711 \brief The RouteQuery type is used to provide query parameters to a
712 RouteModel.
713
714 A RouteQuery is used to pack all the parameters necessary to make a request
715 to a routing service, which can then populate the contents of a RouteModel.
716
717 These parameters describe key details of the route, such as \l waypoints to
718 pass through, \l excludedAreas to avoid, the \l travelModes in use, as well
719 as detailed preferences on how to optimize the route and what features
720 to prefer or avoid along the path (such as toll roads, highways, etc).
721
722 RouteQuery objects are used exclusively to fill out the value of a
723 RouteModel's \l{RouteModel::query}{query} property, which can then begin
724 the retrieval process to populate the model.
725
726 Some plugins might allow or require specific parameters to operate.
727 In order to specify these plugin-specific parameters, MapParameter elements
728 can be nested inside a RouteQuery.
729
730 \section2 Example Usage
731
732 The following snipped shows an incomplete example of creating a RouteQuery
733 object and setting it as the value of a RouteModel's \l{RouteModel::query}{query}
734 property.
735
736 \code
737 RouteQuery {
738 id: aQuery
739 }
740
741 RouteModel {
742 query: aQuery
743 autoUpdate: false
744 }
745 \endcode
746
747 For a more complete example, see the documentation for the \l{RouteModel}
748 type, and the Mapviewer example.
749
750 \sa RouteModel
751
752*/
753
754QDeclarativeGeoRouteQuery::QDeclarativeGeoRouteQuery(QObject *parent)
755: QObject(parent), complete_(false), m_excludedAreaCoordinateChanged(false)
756{
757}
758
759QDeclarativeGeoRouteQuery::QDeclarativeGeoRouteQuery(const QGeoRouteRequest &request, QObject *parent)
760: QObject(parent), request_(request), complete_(false), m_excludedAreaCoordinateChanged(false)
761{
762 // Extra params assumed to be already set in the request.
763 // Init waypoints
764 const QList<QGeoCoordinate> wpts = request_.waypoints();
765 const QList<QVariantMap> meta = request_.waypointsMetadata();
766 for (int i = 0; i < wpts.size(); ++i) {
767 QDeclarativeGeoWaypoint *w = new QDeclarativeGeoWaypoint(this);
768 w->setCoordinate(wpts.at(i));
769 w->setMetadata(meta.at(i));
770 m_waypoints << w;
771 }
772}
773
774QDeclarativeGeoRouteQuery::~QDeclarativeGeoRouteQuery()
775{
776}
777
778/*!
779 \internal
780*/
781void QDeclarativeGeoRouteQuery::componentComplete()
782{
783 complete_ = true;
784}
785
786/*!
787 \qmlproperty QList<FeatureType> RouteQuery::featureTypes
788
789 List of features that will be considered when planning the
790 route. Features with a weight of NeutralFeatureWeight will not be returned.
791
792 \list
793 \li RouteQuery.NoFeature - No features will be taken into account when planning the route
794 \li RouteQuery.TollFeature - Consider tollways when planning the route
795 \li RouteQuery.HighwayFeature - Consider highways when planning the route
796 \li RouteQuery.PublicTransitFeature - Consider public transit when planning the route
797 \li RouteQuery.FerryFeature - Consider ferries when planning the route
798 \li RouteQuery.TunnelFeature - Consider tunnels when planning the route
799 \li RouteQuery.DirtRoadFeature - Consider dirt roads when planning the route
800 \li RouteQuery.ParksFeature - Consider parks when planning the route
801 \li RouteQuery.MotorPoolLaneFeature - Consider motor pool lanes when planning the route
802 \li RouteQuery.TrafficFeature - Consider traffic when planning the route
803 \endlist
804
805 \sa setFeatureWeight, featureWeight
806*/
807
808QList<int> QDeclarativeGeoRouteQuery::featureTypes()
809{
810 QList<int> list;
811
812 for (int i = 0; i < request_.featureTypes().count(); ++i) {
813 list.append(t: static_cast<int>(request_.featureTypes().at(i)));
814 }
815 return list;
816}
817
818/*!
819 \qmlproperty int RouteQuery::numberAlternativeRoutes
820
821 The number of alternative routes requested when requesting routes.
822 The default value is 0.
823*/
824
825
826int QDeclarativeGeoRouteQuery::numberAlternativeRoutes() const
827{
828 return request_.numberAlternativeRoutes();
829}
830
831void QDeclarativeGeoRouteQuery::setNumberAlternativeRoutes(int numberAlternativeRoutes)
832{
833 if (numberAlternativeRoutes == request_.numberAlternativeRoutes())
834 return;
835
836 request_.setNumberAlternativeRoutes(numberAlternativeRoutes);
837
838 if (complete_) {
839 emit numberAlternativeRoutesChanged();
840 emit queryDetailsChanged();
841 }
842}
843
844/*!
845 \qmlproperty list<coordinate> RouteQuery::waypoints
846
847
848 The coordinates of the waypoints for the desired route.
849 The waypoints should be given in order from origin to destination.
850 Two or more coordinates are needed.
851
852 Waypoints can be set as part of the RouteQuery type declaration or
853 dynamically with the functions provided.
854
855 When setting this property to a list of waypoints, each waypoint
856 can be either a \l coordinate or a \l Waypoint, interchangeably.
857 If a \l coordinate is passed, it will be internally converted to a
858 \l Waypoint.
859
860 This property, however, always contains a list of coordinates.
861
862 \sa waypointObjects, addWaypoint, removeWaypoint, clearWaypoints
863*/
864
865QVariantList QDeclarativeGeoRouteQuery::waypoints()
866{
867 QVariantList res;
868
869 for (const auto &w : m_waypoints)
870 res << QVariant::fromValue(value: w->coordinate());
871
872 return res;
873}
874
875/*!
876 \qmlmethod list<Waypoint> QtLocation::RouteQuery::waypointObjects()
877
878 This method can be used to retrieve the list of Waypoint objects
879 relative to RouteQuery::waypoints.
880
881 \sa waypointObjects, addWaypoint, removeWaypoint, clearWaypoints
882*/
883QVariantList QDeclarativeGeoRouteQuery::waypointObjects()
884{
885 QVariantList res;
886
887 for (const auto &w : m_waypoints)
888 res << QVariant::fromValue(value: w);
889
890 return res;
891}
892
893void QDeclarativeGeoRouteQuery::setWaypoints(const QVariantList &value)
894{
895 QList<QDeclarativeGeoWaypoint *> waypointList;
896 bool allWaypoints = true;
897
898 for (const auto &w: value) {
899 // First, test if this is already a QDeclarativeGeoWaypoint
900 // From QVariant to QObject *
901 QDeclarativeGeoWaypoint *waypoint = nullptr;
902 QObject *obj = qvariant_cast<QObject *>(v: w);
903 waypoint = qobject_cast<QDeclarativeGeoWaypoint *>(object: obj);
904
905 if (waypoint) {
906 waypointList << waypoint;
907 continue;
908 }
909
910 // if here, w is not a Waypoint, so either a QGeoCoordinate or a variant map, so a waypoint has to be instantiated.
911 allWaypoints = false;
912
913 QGeoCoordinate c = parseCoordinate(value: w);
914 if (!c.isValid()) {
915 qmlWarning(me: this) << QStringLiteral("Invalid waypoint");
916 flushWaypoints(waypoints&: waypointList);
917 return;
918 }
919
920 waypoint = new QDeclarativeGeoWaypoint(this);
921 waypoint->setCoordinate(c);
922 waypointList << waypoint;
923
924 }
925
926 if (allWaypoints && m_waypoints == waypointList)
927 return;
928
929 flushWaypoints(waypoints&: m_waypoints);
930 m_waypoints = waypointList;
931 for (const QDeclarativeGeoWaypoint *w: qAsConst(t&: m_waypoints))
932 connect(sender: w, signal: &QDeclarativeGeoWaypoint::waypointDetailsChanged, receiver: this, slot: &QDeclarativeGeoRouteQuery::waypointChanged);
933
934 waypointChanged();
935}
936
937/*!
938 \qmlproperty list<georectangle> RouteQuery::excludedAreas
939
940 Areas that the route must not cross.
941
942 Excluded areas can be set as part of the \l RouteQuery type declaration or
943 dynamically with the functions provided.
944
945 \sa addExcludedArea, removeExcludedArea, clearExcludedAreas
946*/
947QJSValue QDeclarativeGeoRouteQuery::excludedAreas() const
948{
949 QQmlContext *context = QQmlEngine::contextForObject(parent());
950 QQmlEngine *engine = context->engine();
951 QV4::ExecutionEngine *v4 = QQmlEnginePrivate::getV4Engine(e: engine);
952
953 QV4::Scope scope(v4);
954 QV4::Scoped<QV4::ArrayObject> excludedAreasArray(scope, v4->newArrayObject(count: request_.excludeAreas().length()));
955 for (int i = 0; i < request_.excludeAreas().length(); ++i) {
956 const QGeoRectangle &r = request_.excludeAreas().at(i);
957
958 QV4::ScopedValue cv(scope, v4->fromVariant(QVariant::fromValue(value: r)));
959 excludedAreasArray->put(idx: i, v: cv);
960 }
961
962 return QJSValue(v4, excludedAreasArray.asReturnedValue());
963}
964
965void QDeclarativeGeoRouteQuery::setExcludedAreas(const QJSValue &value)
966{
967 if (!value.isArray())
968 return;
969
970 QList<QGeoRectangle> excludedAreasList;
971 quint32 length = value.property(QStringLiteral("length")).toUInt();
972 for (quint32 i = 0; i < length; ++i) {
973 bool ok;
974 QGeoRectangle r = parseRectangle(value: value.property(arrayIndex: i), ok: &ok);
975
976 if (!ok || !r.isValid()) {
977 qmlWarning(me: this) << QStringLiteral("Unsupported area type");
978 return;
979 }
980
981 excludedAreasList.append(t: r);
982 }
983
984 if (request_.excludeAreas() == excludedAreasList)
985 return;
986
987 request_.setExcludeAreas(excludedAreasList);
988
989 if (complete_) {
990 emit excludedAreasChanged();
991 emit queryDetailsChanged();
992 }
993}
994
995/*!
996 \qmlmethod void QtLocation::RouteQuery::addExcludedArea(georectangle area)
997
998 Adds the specified georectangle \a area to the excluded areas
999 (areas that the route must not cross).
1000 The same area can only be added once.
1001
1002 \sa removeExcludedArea, clearExcludedAreas
1003*/
1004
1005
1006void QDeclarativeGeoRouteQuery::addExcludedArea(const QGeoRectangle &area)
1007{
1008 if (!area.isValid())
1009 return;
1010
1011 QList<QGeoRectangle> excludedAreas = request_.excludeAreas();
1012
1013 if (excludedAreas.contains(t: area))
1014 return;
1015
1016 excludedAreas.append(t: area);
1017
1018 request_.setExcludeAreas(excludedAreas);
1019
1020 if (complete_) {
1021 emit excludedAreasChanged();
1022 emit queryDetailsChanged();
1023 }
1024}
1025
1026/*!
1027 \qmlmethod void QtLocation::RouteQuery::removeExcludedArea(georectangle area)
1028
1029 Removes the given \a area from excluded areas (areas that the route must not cross).
1030
1031 \sa addExcludedArea, clearExcludedAreas
1032*/
1033
1034void QDeclarativeGeoRouteQuery::removeExcludedArea(const QGeoRectangle &area)
1035{
1036 if (!area.isValid())
1037 return;
1038
1039 QList<QGeoRectangle> excludedAreas = request_.excludeAreas();
1040
1041 int index = excludedAreas.lastIndexOf(t: area);
1042 if (index == -1) {
1043 qmlWarning(me: this) << QStringLiteral("Cannot remove nonexistent area.");
1044 return;
1045 }
1046 excludedAreas.removeAt(i: index);
1047 request_.setExcludeAreas(excludedAreas);
1048
1049 if (complete_) {
1050 emit excludedAreasChanged();
1051 emit queryDetailsChanged();
1052 }
1053}
1054
1055/*!
1056 \qmlmethod void QtLocation::RouteQuery::clearExcludedAreas()
1057
1058 Clears all excluded areas (areas that the route must not cross).
1059
1060 \sa addExcludedArea, removeExcludedArea
1061*/
1062
1063void QDeclarativeGeoRouteQuery::clearExcludedAreas()
1064{
1065 if (request_.excludeAreas().isEmpty())
1066 return;
1067
1068 request_.setExcludeAreas(QList<QGeoRectangle>());
1069
1070 if (complete_) {
1071 emit excludedAreasChanged();
1072 emit queryDetailsChanged();
1073 }
1074}
1075
1076/*!
1077 \qmlmethod void QtLocation::RouteQuery::addWaypoint(coordinate)
1078
1079 Appends a coordinate to the list of waypoints. Same coordinate
1080 can be set multiple times.
1081 The \a coordinate argument can be a \l coordinate or a \l Waypoint.
1082 If a \l coordinate is used, it will be internally converted to a
1083 \l Waypoint.
1084
1085 \sa removeWaypoint, clearWaypoints
1086*/
1087void QDeclarativeGeoRouteQuery::addWaypoint(const QVariant &waypoint)
1088{
1089 QDeclarativeGeoWaypoint *w = nullptr;
1090 QObject *obj = qvariant_cast<QObject *>(v: waypoint);
1091 w = qobject_cast<QDeclarativeGeoWaypoint *>(object: obj);
1092
1093 if (w) {
1094 if (! w->isValid()) {
1095 qmlWarning(me: this) << QStringLiteral("Invalid waypoint");
1096 return;
1097 }
1098
1099 m_waypoints << w;
1100 connect(sender: w, signal: &QDeclarativeGeoWaypoint::waypointDetailsChanged, receiver: this, slot: &QDeclarativeGeoRouteQuery::waypointChanged);
1101 waypointChanged();
1102 return;
1103 }
1104
1105 // if here, waypoint is not a Waypoint, so either a QGeoCoordinate or a variant map, so a waypoint has to be instantiated.
1106
1107 QGeoCoordinate c = parseCoordinate(value: waypoint);
1108 if (!c.isValid()) {
1109 qmlWarning(me: this) << QStringLiteral("Invalid coordinate as waypoint");
1110 return;
1111 }
1112
1113 w = new QDeclarativeGeoWaypoint(this);
1114 w->setCoordinate(c);
1115 m_waypoints << w;
1116 connect(sender: w, signal: &QDeclarativeGeoWaypoint::waypointDetailsChanged, receiver: this, slot: &QDeclarativeGeoRouteQuery::waypointChanged);
1117 waypointChanged();
1118}
1119
1120/*!
1121 \qmlmethod void QtLocation::RouteQuery::removeWaypoint(coordinate)
1122
1123 Removes the given \a coordinate from the list of waypoints. If the
1124 same coordinate appears multiple times, the most recently added
1125 coordinate instance is removed.
1126
1127 \sa addWaypoint, clearWaypoints
1128*/
1129void QDeclarativeGeoRouteQuery::removeWaypoint(const QVariant &waypoint)
1130{
1131 QDeclarativeGeoWaypoint *w = nullptr;
1132 QObject *obj = qvariant_cast<QObject *>(v: waypoint);
1133 w = qobject_cast<QDeclarativeGeoWaypoint *>(object: obj);
1134
1135 if (w) {
1136 if (!w->isValid()) {
1137 qmlWarning(me: this) << QStringLiteral("Invalid waypoint");
1138 return;
1139 }
1140
1141 int idx = findWaypoint(waypoints: m_waypoints, w);
1142 if (idx >= 0) {
1143 QDeclarativeGeoWaypoint *toRemove = m_waypoints.takeAt(i: idx);
1144 toRemove->disconnect(receiver: this);
1145 if (toRemove->parent() == this)
1146 delete toRemove;
1147
1148 waypointChanged();
1149 } else {
1150 qmlWarning(me: this) << QStringLiteral("Cannot remove nonexistent waypoint.");
1151 }
1152 return;
1153 }
1154
1155 QGeoCoordinate c = parseCoordinate(value: waypoint);
1156 if (!c.isValid()) {
1157 qmlWarning(me: this) << QStringLiteral("Invalid coordinate as waypoint");
1158 return;
1159 }
1160
1161 int idx = findWaypoint(waypoints: m_waypoints, c);
1162 if (idx >= 0) {
1163 QDeclarativeGeoWaypoint *toRemove = m_waypoints.takeAt(i: idx);
1164 toRemove->disconnect(receiver: this);
1165 if (toRemove->parent() == this)
1166 delete toRemove;
1167
1168 waypointChanged();
1169 } else {
1170 qmlWarning(me: this) << QStringLiteral("Cannot remove nonexistent waypoint.");
1171 }
1172}
1173
1174/*!
1175 \qmlmethod void QtLocation::RouteQuery::clearWaypoints()
1176
1177 Clears all waypoints.
1178
1179 \sa removeWaypoint, addWaypoint
1180*/
1181void QDeclarativeGeoRouteQuery::clearWaypoints()
1182{
1183 if (m_waypoints.isEmpty())
1184 return;
1185
1186 flushWaypoints(waypoints&: m_waypoints);
1187 waypointChanged();
1188}
1189
1190void QDeclarativeGeoRouteQuery::flushWaypoints(QList<QDeclarativeGeoWaypoint *> &waypoints)
1191{
1192 for (const QDeclarativeGeoWaypoint *w : qAsConst(t&: waypoints)) {
1193 w->disconnect(receiver: this);
1194 if (w->parent() == this) // w has been created internally as a result of adding a QGeoCoordinate
1195 delete w;
1196 }
1197 waypoints.clear();
1198}
1199
1200/*!
1201 \qmlmethod void QtLocation::RouteQuery::setFeatureWeight(FeatureType feature, FeatureWeight weight)
1202
1203 Defines the \a weight to associate with a \a feature during the planning
1204 of a route.
1205
1206 Following lists the possible feature weights:
1207
1208 \value RouteQuery.NeutralFeatureWeight
1209 The presence or absence of the feature does not affect the planning of the
1210 route
1211
1212 \value RouteQuery.PreferFeatureWeight
1213 Routes which contain the feature are preferred over those that do not
1214
1215 \value RouteQuery.RequireFeatureWeight
1216 Only routes which contain the feature are considered, otherwise no
1217 route will be returned
1218
1219 \value RouteQuery.AvoidFeatureWeight
1220 Routes which do not contain the feature are preferred over those that
1221 do
1222
1223 \value RouteQuery.DisallowFeatureWeight
1224 Only routes which do not contain the feature are considered, otherwise
1225 no route will be returned
1226
1227 \sa featureTypes, resetFeatureWeights, featureWeight
1228
1229*/
1230
1231void QDeclarativeGeoRouteQuery::setFeatureWeight(FeatureType featureType, FeatureWeight featureWeight)
1232{
1233 if (featureType == NoFeature && !request_.featureTypes().isEmpty()) {
1234 resetFeatureWeights();
1235 return;
1236 }
1237
1238 // Check if the weight changes, as we need to signal it
1239 FeatureWeight originalWeight = static_cast<FeatureWeight>(request_.featureWeight(featureType: static_cast<QGeoRouteRequest::FeatureType>(featureType)));
1240 if (featureWeight == originalWeight)
1241 return;
1242
1243 request_.setFeatureWeight(featureType: static_cast<QGeoRouteRequest::FeatureType>(featureType),
1244 featureWeight: static_cast<QGeoRouteRequest::FeatureWeight>(featureWeight));
1245 if (complete_ && ((originalWeight == NeutralFeatureWeight) || (featureWeight == NeutralFeatureWeight))) {
1246 // featureTypes should now give a different list, because the original and new weight
1247 // were not same, and other one was neutral weight
1248 emit featureTypesChanged();
1249 emit queryDetailsChanged();
1250 }
1251}
1252
1253/*!
1254 \qmlmethod void QtLocation::RouteQuery::resetFeatureWeights()
1255
1256 Resets all feature weights to their default state (NeutralFeatureWeight).
1257
1258 \sa featureTypes, setFeatureWeight, featureWeight
1259*/
1260void QDeclarativeGeoRouteQuery::resetFeatureWeights()
1261{
1262 // reset all feature types.
1263 QList<QGeoRouteRequest::FeatureType> featureTypes = request_.featureTypes();
1264 for (int i = 0; i < featureTypes.count(); ++i) {
1265 request_.setFeatureWeight(featureType: featureTypes.at(i), featureWeight: QGeoRouteRequest::NeutralFeatureWeight);
1266 }
1267 if (complete_) {
1268 emit featureTypesChanged();
1269 emit queryDetailsChanged();
1270 }
1271}
1272
1273/*!
1274 \qmlmethod FeatureWeight QtLocation::RouteQuery::featureWeight(FeatureType featureType)
1275
1276 Gets the weight for the \a featureType.
1277
1278 \sa featureTypes, setFeatureWeight, resetFeatureWeights
1279*/
1280
1281int QDeclarativeGeoRouteQuery::featureWeight(FeatureType featureType)
1282{
1283 return request_.featureWeight(featureType: static_cast<QGeoRouteRequest::FeatureType>(featureType));
1284}
1285
1286/*!
1287 \internal
1288*/
1289void QDeclarativeGeoRouteQuery::setTravelModes(QDeclarativeGeoRouteQuery::TravelModes travelModes)
1290{
1291 QGeoRouteRequest::TravelModes reqTravelModes;
1292
1293 if (travelModes & QDeclarativeGeoRouteQuery::CarTravel)
1294 reqTravelModes |= QGeoRouteRequest::CarTravel;
1295 if (travelModes & QDeclarativeGeoRouteQuery::PedestrianTravel)
1296 reqTravelModes |= QGeoRouteRequest::PedestrianTravel;
1297 if (travelModes & QDeclarativeGeoRouteQuery::BicycleTravel)
1298 reqTravelModes |= QGeoRouteRequest::BicycleTravel;
1299 if (travelModes & QDeclarativeGeoRouteQuery::PublicTransitTravel)
1300 reqTravelModes |= QGeoRouteRequest::PublicTransitTravel;
1301 if (travelModes & QDeclarativeGeoRouteQuery::TruckTravel)
1302 reqTravelModes |= QGeoRouteRequest::TruckTravel;
1303
1304 if (reqTravelModes == request_.travelModes())
1305 return;
1306
1307 request_.setTravelModes(reqTravelModes);
1308
1309 if (complete_) {
1310 emit travelModesChanged();
1311 emit queryDetailsChanged();
1312 }
1313}
1314
1315
1316/*!
1317 \qmlproperty enumeration RouteQuery::segmentDetail
1318
1319 The level of detail which will be used in the representation of routing segments.
1320
1321 \value RouteQuery.NoSegmentData
1322 No segment data should be included with the route
1323
1324 \value RouteQuery.BasicSegmentData
1325 Basic segment data will be included with the route
1326
1327 The default value is \c {RouteQuery.BasicSegmentData}.
1328*/
1329
1330void QDeclarativeGeoRouteQuery::setSegmentDetail(SegmentDetail segmentDetail)
1331{
1332 if (static_cast<QGeoRouteRequest::SegmentDetail>(segmentDetail) == request_.segmentDetail())
1333 return;
1334 request_.setSegmentDetail(static_cast<QGeoRouteRequest::SegmentDetail>(segmentDetail));
1335 if (complete_) {
1336 emit segmentDetailChanged();
1337 emit queryDetailsChanged();
1338 }
1339}
1340
1341QDeclarativeGeoRouteQuery::SegmentDetail QDeclarativeGeoRouteQuery::segmentDetail() const
1342{
1343 return static_cast<QDeclarativeGeoRouteQuery::SegmentDetail>(request_.segmentDetail());
1344}
1345
1346/*!
1347 \qmlproperty enumeration RouteQuery::maneuverDetail
1348
1349 The level of detail which will be used in the representation of routing maneuvers.
1350
1351 \value RouteQuery.NoManeuvers
1352 No maneuvers should be included with the route
1353
1354 \value RouteQuery.BasicManeuvers
1355 Basic maneuvers will be included with the route
1356
1357 The default value is \c {RouteQuery.BasicManeuvers}.
1358*/
1359
1360void QDeclarativeGeoRouteQuery::setManeuverDetail(ManeuverDetail maneuverDetail)
1361{
1362 if (static_cast<QGeoRouteRequest::ManeuverDetail>(maneuverDetail) == request_.maneuverDetail())
1363 return;
1364 request_.setManeuverDetail(static_cast<QGeoRouteRequest::ManeuverDetail>(maneuverDetail));
1365 if (complete_) {
1366 emit maneuverDetailChanged();
1367 emit queryDetailsChanged();
1368 }
1369}
1370
1371QDeclarativeGeoRouteQuery::ManeuverDetail QDeclarativeGeoRouteQuery::maneuverDetail() const
1372{
1373 return static_cast<QDeclarativeGeoRouteQuery::ManeuverDetail>(request_.maneuverDetail());
1374}
1375
1376/*!
1377 \qmlproperty enumeration RouteQuery::travelModes
1378
1379 The travel modes which should be considered during the planning of the route.
1380 Values can be combined with OR ('|') -operator.
1381
1382 \value RouteQuery.CarTravel
1383 The route will be optimized for someone who is driving a car
1384
1385 \value RouteQuery.PedestrianTravel
1386 The route will be optimized for someone who is walking
1387
1388 \value RouteQuery.BicycleTravel
1389 The route will be optimized for someone who is riding a bicycle
1390
1391 \value RouteQuery.PublicTransit
1392 Travel The route will be optimized for someone who is making use of public transit
1393
1394 \value RouteQuery.TruckTravel
1395 The route will be optimized for someone who is driving a truck
1396
1397 The default value is \c {RouteQuery.CarTravel}.
1398*/
1399
1400QDeclarativeGeoRouteQuery::TravelModes QDeclarativeGeoRouteQuery::travelModes() const
1401{
1402 QGeoRouteRequest::TravelModes reqTravelModes = request_.travelModes();
1403 QDeclarativeGeoRouteQuery::TravelModes travelModes;
1404
1405 if (reqTravelModes & QGeoRouteRequest::CarTravel)
1406 travelModes |= QDeclarativeGeoRouteQuery::CarTravel;
1407 if (reqTravelModes & QGeoRouteRequest::PedestrianTravel)
1408 travelModes |= QDeclarativeGeoRouteQuery::PedestrianTravel;
1409 if (reqTravelModes & QGeoRouteRequest::BicycleTravel)
1410 travelModes |= QDeclarativeGeoRouteQuery::BicycleTravel;
1411 if (reqTravelModes & QGeoRouteRequest::PublicTransitTravel)
1412 travelModes |= QDeclarativeGeoRouteQuery::PublicTransitTravel;
1413 if (reqTravelModes & QGeoRouteRequest::TruckTravel)
1414 travelModes |= QDeclarativeGeoRouteQuery::TruckTravel;
1415
1416 return travelModes;
1417}
1418
1419/*!
1420 \qmlproperty enumeration RouteQuery::routeOptimizations
1421
1422 The route optimizations which should be considered during the planning of the route.
1423 Values can be combined with OR ('|') -operator.
1424
1425
1426 \value RouteQuery.ShortestRoute
1427 Minimize the length of the journey
1428
1429 \value RouteQuery.FastestRoute
1430 Minimize the traveling time for the journey
1431
1432 \value RouteQuery.MostEconomicRoute
1433 Minimize the cost of the journey
1434
1435 \value RouteQuery.MostScenicRoute
1436 Maximize the scenic potential of the journey
1437
1438 The default value is \c {RouteQuery.FastestRoute}.
1439*/
1440
1441QDeclarativeGeoRouteQuery::RouteOptimizations QDeclarativeGeoRouteQuery::routeOptimizations() const
1442{
1443 QGeoRouteRequest::RouteOptimizations reqOptimizations = request_.routeOptimization();
1444 QDeclarativeGeoRouteQuery::RouteOptimizations optimization;
1445
1446 if (reqOptimizations & QGeoRouteRequest::ShortestRoute)
1447 optimization |= QDeclarativeGeoRouteQuery::ShortestRoute;
1448 if (reqOptimizations & QGeoRouteRequest::FastestRoute)
1449 optimization |= QDeclarativeGeoRouteQuery::FastestRoute;
1450 if (reqOptimizations & QGeoRouteRequest::MostEconomicRoute)
1451 optimization |= QDeclarativeGeoRouteQuery::MostEconomicRoute;
1452 if (reqOptimizations & QGeoRouteRequest::MostScenicRoute)
1453 optimization |= QDeclarativeGeoRouteQuery::MostScenicRoute;
1454
1455 return optimization;
1456}
1457
1458/*!
1459 \qmlproperty date RouteQuery::departureTime
1460
1461 The departure time to be used when querying for the route.
1462 The default value is an invalid date, meaning no departure time will be used in the query.
1463
1464 \since 5.13
1465*/
1466void QDeclarativeGeoRouteQuery::setDepartureTime(const QDateTime &departureTime)
1467{
1468 if (departureTime == request_.departureTime())
1469 return;
1470
1471 request_.setDepartureTime(departureTime);
1472 if (complete_) {
1473 emit departureTimeChanged();
1474 emit queryDetailsChanged();
1475 }
1476}
1477
1478QDateTime QDeclarativeGeoRouteQuery::departureTime() const
1479{
1480 return request_.departureTime();
1481}
1482
1483void QDeclarativeGeoRouteQuery::setRouteOptimizations(QDeclarativeGeoRouteQuery::RouteOptimizations optimization)
1484{
1485 QGeoRouteRequest::RouteOptimizations reqOptimizations;
1486
1487 if (optimization & QDeclarativeGeoRouteQuery::ShortestRoute)
1488 reqOptimizations |= QGeoRouteRequest::ShortestRoute;
1489 if (optimization & QDeclarativeGeoRouteQuery::FastestRoute)
1490 reqOptimizations |= QGeoRouteRequest::FastestRoute;
1491 if (optimization & QDeclarativeGeoRouteQuery::MostEconomicRoute)
1492 reqOptimizations |= QGeoRouteRequest::MostEconomicRoute;
1493 if (optimization & QDeclarativeGeoRouteQuery::MostScenicRoute)
1494 reqOptimizations |= QGeoRouteRequest::MostScenicRoute;
1495
1496 if (reqOptimizations == request_.routeOptimization())
1497 return;
1498
1499 request_.setRouteOptimization(reqOptimizations);
1500
1501 if (complete_) {
1502 emit routeOptimizationsChanged();
1503 emit queryDetailsChanged();
1504 }
1505}
1506
1507/*!
1508 \internal
1509*/
1510QGeoRouteRequest QDeclarativeGeoRouteQuery::routeRequest()
1511{
1512 if (m_extraParametersChanged) {
1513 m_extraParametersChanged = false;
1514 // Update extra params into request
1515 const QList<QDeclarativeGeoMapParameter *> params = quickChildren<QDeclarativeGeoMapParameter>();
1516 QVariantMap extraParameters;
1517 for (const QDeclarativeGeoMapParameter *p: params)
1518 extraParameters[p->type()] = p->toVariantMap();
1519 request_.setExtraParameters(extraParameters);
1520 }
1521 if (m_waypointsChanged) {
1522 m_waypointsChanged = false;
1523 // Update waypoints and metadata into request
1524 request_.setWaypoints(waypointCoordinates(waypoints: m_waypoints));
1525 request_.setWaypointsMetadata(waypointMetadata(waypoints: m_waypoints));
1526 }
1527 return request_;
1528}
1529
1530
1531/*!
1532 \qmlproperty VariantMap RouteQuery::extraParameters
1533 \readonly
1534
1535 The route query extra parameters. This property is read only. If the query is
1536 defined by the user, these can be set by using MapParameters.
1537 If the route query comes from the engine via signals, the query is intended to be read-only.
1538
1539 \since 5.11
1540*/
1541QVariantMap QDeclarativeGeoRouteQuery::extraParameters()
1542{
1543 return routeRequest().extraParameters();
1544}
1545
1546void QDeclarativeGeoRouteQuery::excludedAreaCoordinateChanged()
1547{
1548 if (!m_excludedAreaCoordinateChanged) {
1549 m_excludedAreaCoordinateChanged = true;
1550 QMetaObject::invokeMethod(obj: this, member: "doCoordinateChanged", type: Qt::QueuedConnection);
1551 }
1552}
1553
1554void QDeclarativeGeoRouteQuery::extraParameterChanged()
1555{
1556 m_extraParametersChanged = true;
1557 if (complete_) {
1558 emit extraParametersChanged();
1559 emit queryDetailsChanged();
1560 }
1561}
1562
1563void QDeclarativeGeoRouteQuery::waypointChanged()
1564{
1565 m_waypointsChanged = true;
1566 if (complete_) {
1567 emit waypointsChanged();
1568 emit queryDetailsChanged();
1569 }
1570}
1571
1572void QDeclarativeGeoRouteQuery::append(QQmlListProperty<QObject> *p, QObject *v)
1573{
1574 QDeclarativeGeoRouteQuery *query = static_cast<QDeclarativeGeoRouteQuery*>(p->object);
1575 query->m_children.append(t: v);
1576
1577 QDeclarativeGeoMapParameter *param = qobject_cast<QDeclarativeGeoMapParameter *>(object: v);
1578 if (param) {
1579 query->m_extraParametersChanged = true;
1580 query->connect(sender: param, signal: &QGeoMapParameter::propertyUpdated,
1581 receiver: query, slot: &QDeclarativeGeoRouteQuery::extraParameterChanged);
1582 if (query->complete_) {
1583 emit query->extraParametersChanged();
1584 emit query->queryDetailsChanged();
1585 }
1586 }
1587}
1588
1589int QDeclarativeGeoRouteQuery::count(QQmlListProperty<QObject> *p)
1590{
1591 return static_cast<QDeclarativeGeoRouteQuery*>(p->object)->m_children.count();
1592}
1593
1594QObject *QDeclarativeGeoRouteQuery::at(QQmlListProperty<QObject> *p, int idx)
1595{
1596 return static_cast<QDeclarativeGeoRouteQuery*>(p->object)->m_children.at(i: idx);
1597}
1598
1599void QDeclarativeGeoRouteQuery::clear(QQmlListProperty<QObject> *p)
1600{
1601 QDeclarativeGeoRouteQuery *query = static_cast<QDeclarativeGeoRouteQuery*>(p->object);
1602 for (auto kid : qAsConst(t&: query->m_children)) {
1603 auto val = qobject_cast<QDeclarativeGeoMapParameter *>(object: kid);
1604 if (val) {
1605 val->disconnect(sender: val, signal: nullptr, receiver: query, member: nullptr);
1606 query->m_extraParametersChanged = true;
1607 }
1608 }
1609 query->m_children.clear();
1610 if (query->m_extraParametersChanged && query->complete_) {
1611 emit query->extraParametersChanged();
1612 emit query->queryDetailsChanged();
1613 }
1614}
1615
1616QQmlListProperty<QObject> QDeclarativeGeoRouteQuery::declarativeChildren()
1617{
1618 return QQmlListProperty<QObject>(this, nullptr,
1619 &QDeclarativeGeoRouteQuery::append,
1620 &QDeclarativeGeoRouteQuery::count,
1621 &QDeclarativeGeoRouteQuery::at,
1622 &QDeclarativeGeoRouteQuery::clear);
1623}
1624
1625void QDeclarativeGeoRouteQuery::doCoordinateChanged()
1626{
1627 m_excludedAreaCoordinateChanged = false;
1628 if (complete_)
1629 emit queryDetailsChanged();
1630}
1631
1632/*!
1633 \qmltype Waypoint
1634 \instantiates QDeclarativeGeoWaypoint
1635 \inqmlmodule QtLocation
1636 \ingroup qml-QtLocation5-routing
1637 \since QtLocation 5.11
1638
1639 \brief The Waypoint type provides a mean to specify a waypoint in a \l RouteQuery
1640 in a more detailed way than by using a simple \l coordinate.
1641
1642 A Waypoint is a type that allows to specify properties of a waypoint in a \l RouteQuery,
1643 such as the waypoint coordinate, or the angle of approach to the waypoint.
1644
1645 Additional information that are backend-specific can be specified by nesting \l MapParameter
1646 elements.
1647
1648 Changing properties of the waypoint or of its nested MapParameteters will cause the containing
1649 \l RouteQuery to emit the queryDetailsChanged signal.
1650
1651 \section2 Example Usage
1652
1653 \code
1654 Plugin {
1655 id: aPlugin
1656 name: "osm"
1657 }
1658
1659 Waypoint {
1660 id: waypointStart
1661 coordinate: ...
1662 bearing: ...
1663 }
1664 Waypoint {
1665 id: waypointFinish
1666 coordinate: ...
1667 bearing: ...
1668 }
1669
1670 RouteQuery {
1671 id: aQuery
1672 Component.onCompleted: {
1673 travelModes = RouteQuery.CarTravel
1674 addWaypoint(waypointStart)
1675 var aWaypoint = Qt.createQmlObject ('import QtLocation 5.11; Waypoint { ... }', ...)
1676 addWaypoint(aWaypoint)
1677 addWaypoint(waypointFinish)
1678 }
1679 }
1680
1681 RouteModel {
1682 id: routeModel
1683 plugin: aPlugin
1684 query: aQuery
1685 autoUpdate: true
1686 }
1687 \endcode
1688
1689 \sa RouteQuery
1690*/
1691
1692
1693/*
1694 *
1695 At the time of adding this class (2017.11), 3 routing services are natively supported in Qt: Esri, Here and OSRM.
1696 Waypoint documentation for each of these:
1697 Esri: http://resources.arcgis.com/en/help/arcgis-rest-api/index.html#//02r300000036000000 , called "stop"
1698 HERE: https://developer.here.com/documentation/routing/topics/resource-param-type-waypoint.html
1699 OSRM: https://github.com/Project-OSRM/osrm-backend/blob/master/docs/http.md , under Request Options
1700 *
1701 */
1702
1703
1704static QGeoCoordinate convertWaypointToCoordinate(const QDeclarativeGeoWaypoint *value)
1705{
1706 return value->coordinate();
1707}
1708
1709struct WaypointVariantConversions
1710{
1711 WaypointVariantConversions()
1712 {
1713 QMetaType::registerConverter<QDeclarativeGeoWaypoint *, QGeoCoordinate>(function: convertWaypointToCoordinate);
1714 }
1715};
1716
1717Q_GLOBAL_STATIC(WaypointVariantConversions, initWaypointConversions)
1718
1719
1720QDeclarativeGeoWaypoint::QDeclarativeGeoWaypoint(QObject *parent) : QGeoCoordinateObject(parent)
1721{
1722 initWaypointConversions();
1723 connect(sender: this, signal: &QGeoCoordinateObject::coordinateChanged,
1724 receiver: this, slot: &QDeclarativeGeoWaypoint::waypointDetailsChanged);
1725}
1726
1727QDeclarativeGeoWaypoint::~QDeclarativeGeoWaypoint()
1728{
1729
1730}
1731
1732bool QDeclarativeGeoWaypoint::operator==(const QDeclarativeGeoWaypoint &other) const
1733{
1734 const QList<QDeclarativeGeoMapParameter *> params = quickChildren<QDeclarativeGeoMapParameter>();
1735 const QList<QDeclarativeGeoMapParameter *> otherParams = other.quickChildren<QDeclarativeGeoMapParameter>();
1736
1737 return coordinate() == other.coordinate() &&
1738 compareFloats(a: m_bearing, b: other.bearing()) &&
1739 compareParameterList(a: params, b: otherParams);
1740}
1741
1742/*!
1743 \qmlproperty coordinate Waypoint::coordinate
1744
1745 The waypoint's coordinate. The default value is undefined.
1746*/
1747
1748
1749/*!
1750 \qmlproperty real Waypoint::latitude
1751
1752 The latitude of the waypoint's coordinate. The default value is NaN.
1753 Changing this property will affect the \l Waypoint::coordinate property as well.
1754*/
1755qreal QDeclarativeGeoWaypoint::latitude() const
1756{
1757 return m_coordinate.latitude();
1758}
1759
1760void QDeclarativeGeoWaypoint::setLatitude(qreal latitude)
1761{
1762 if (compareFloats(a: latitude, b: m_coordinate.latitude()))
1763 return;
1764
1765 m_coordinate.setLatitude(latitude);
1766 if (m_complete) {
1767 emit coordinateChanged();
1768 emit waypointDetailsChanged();
1769 }
1770}
1771
1772/*!
1773 \qmlproperty real Waypoint::longitude
1774
1775 The longitude of the waypoint's coordinate. The default value is NaN.
1776 Changing this property will affect the \l Waypoint::coordinate property as well.
1777*/
1778qreal QDeclarativeGeoWaypoint::longitude() const
1779{
1780 return m_coordinate.longitude();
1781}
1782
1783void QDeclarativeGeoWaypoint::setLongitude(qreal longitude)
1784{
1785 if (compareFloats(a: longitude, b: m_coordinate.longitude()))
1786 return;
1787
1788 m_coordinate.setLongitude(longitude);
1789 if (m_complete) {
1790 emit coordinateChanged();
1791 emit waypointDetailsChanged();
1792 }
1793}
1794
1795/*!
1796 \qmlproperty real Waypoint::altitude
1797
1798 The altitude of the waypoint's coordinate. The default value is NaN.
1799 Changing this property will affect the \l Waypoint::coordinate property as well.
1800*/
1801qreal QDeclarativeGeoWaypoint::altitude() const
1802{
1803 return m_coordinate.altitude();
1804}
1805
1806void QDeclarativeGeoWaypoint::setAltitude(qreal altitude)
1807{
1808 if (compareFloats(a: altitude, b: m_coordinate.altitude()))
1809 return;
1810
1811 m_coordinate.setAltitude(altitude);
1812 if (m_complete) {
1813 emit coordinateChanged();
1814 emit waypointDetailsChanged();
1815 }
1816}
1817
1818bool QDeclarativeGeoWaypoint::isValid() const
1819{
1820 return m_coordinate.isValid();
1821}
1822
1823/*!
1824 \qmlproperty real Waypoint::bearing
1825
1826 The bearing specifying the angle of approach of the waypoint, that is the bearing with which the waypoint is to be approached.
1827 This information may be used by the provider to filter the road segment the waypoint will be placed on, and,
1828 depending on the provider and the \l {QGeoRouteRequest::TravelMode} {travel mode} used, to restrict the maneuvers
1829 allowed at the waypoint, potentially making the provider calculating and returning a different route.
1830
1831 If set to NaN, this value will not be considered.
1832
1833 The default value is NaN.
1834*/
1835qreal QDeclarativeGeoWaypoint::bearing() const
1836{
1837 return m_bearing;
1838}
1839
1840void QDeclarativeGeoWaypoint::setBearing(qreal bearing)
1841{
1842 if (compareFloats(a: bearing, b: m_bearing))
1843 return;
1844
1845 m_bearing = bearing;
1846
1847 // Bearing is actually packed into QGeoRouteRequest::waypointMetadata() together with the extra parameters
1848 m_metadataChanged = true;
1849 if (m_complete) {
1850 emit bearingChanged();
1851 emit waypointDetailsChanged();
1852 }
1853}
1854
1855/*!
1856 \qmlproperty VariantMap Waypoint::metadata
1857 \readonly
1858
1859 The waypoint metadata. This property is read only. If the waypoint is
1860 defined by the user, these can be set by using MapParameters.
1861 If the waypoint comes from the engine via signals, or as part of a read-only route query,
1862 the waypoint is intended to be read-only.
1863*/
1864QVariantMap QDeclarativeGeoWaypoint::metadata()
1865{
1866 if (m_metadataChanged) {
1867 m_metadataChanged = false;
1868 m_metadata.clear();
1869 // Update metadata
1870 const QList<QDeclarativeGeoMapParameter *> params = quickChildren<QDeclarativeGeoMapParameter>();
1871 QVariantMap extraParameters;
1872 for (const QDeclarativeGeoMapParameter *p: params)
1873 extraParameters[p->type()] = p->toVariantMap();
1874 m_metadata[QStringLiteral("extra")] = extraParameters;
1875 m_metadata[QStringLiteral("bearing")] = m_bearing;
1876 }
1877 return m_metadata;
1878}
1879
1880// Used only by QDeclarativeGeoRouteRequest
1881void QDeclarativeGeoWaypoint::setMetadata(const QVariantMap &meta)
1882{
1883 m_metadata = meta;
1884 if (m_metadata.contains(QStringLiteral("bearing")) && m_metadata.value(QStringLiteral("bearing")).canConvert<double>())
1885 m_bearing = m_metadata.value(QStringLiteral("bearing")).toDouble();
1886 m_metadataChanged = false;
1887}
1888
1889void QDeclarativeGeoWaypoint::extraParameterChanged()
1890{
1891 m_metadataChanged = true;
1892 if (m_complete) {
1893 emit extraParametersChanged();
1894 emit waypointDetailsChanged();
1895 }
1896}
1897
1898void QDeclarativeGeoWaypoint::append(QQmlListProperty<QObject> *p, QObject *v)
1899{
1900 QDeclarativeGeoWaypoint *waypoint = static_cast<QDeclarativeGeoWaypoint*>(p->object);
1901 waypoint->m_children.append(t: v);
1902
1903 QDeclarativeGeoMapParameter *param = qobject_cast<QDeclarativeGeoMapParameter *>(object: v);
1904 if (param) {
1905 waypoint->connect(sender: param, signal: &QGeoMapParameter::propertyUpdated,
1906 receiver: waypoint, slot: &QDeclarativeGeoWaypoint::extraParameterChanged);
1907 waypoint->extraParameterChanged();
1908 }
1909}
1910
1911int QDeclarativeGeoWaypoint::count(QQmlListProperty<QObject> *p)
1912{
1913 return static_cast<QDeclarativeGeoWaypoint*>(p->object)->m_children.count();
1914}
1915
1916QObject *QDeclarativeGeoWaypoint::at(QQmlListProperty<QObject> *p, int idx)
1917{
1918 return static_cast<QDeclarativeGeoWaypoint*>(p->object)->m_children.at(i: idx);
1919}
1920
1921void QDeclarativeGeoWaypoint::clear(QQmlListProperty<QObject> *p)
1922{
1923 QDeclarativeGeoWaypoint *waypoint = static_cast<QDeclarativeGeoWaypoint*>(p->object);
1924 for (auto kid : qAsConst(t&: waypoint->m_children)) {
1925 auto val = qobject_cast<QDeclarativeGeoMapParameter *>(object: kid);
1926 if (val) {
1927 val->disconnect(receiver: waypoint);
1928 waypoint->m_metadataChanged = true;
1929 }
1930 }
1931 waypoint->m_children.clear();
1932 if (waypoint->m_metadataChanged && waypoint->m_complete) {
1933 emit waypoint->extraParametersChanged();
1934 emit waypoint->waypointDetailsChanged();
1935 }
1936}
1937
1938QQmlListProperty<QObject> QDeclarativeGeoWaypoint::declarativeChildren()
1939{
1940 return QQmlListProperty<QObject>(this, nullptr,
1941 &QDeclarativeGeoWaypoint::append,
1942 &QDeclarativeGeoWaypoint::count,
1943 &QDeclarativeGeoWaypoint::at,
1944 &QDeclarativeGeoWaypoint::clear);
1945}
1946
1947QT_END_NAMESPACE
1948

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