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 "qdeclarativegeoroute_p.h"
38 #include "locationvaluetypehelper_p.h"
39 #include <QtLocation/private/qgeomap_p.h>
40 #include <QtLocation/private/qgeoroute_p.h>
41 #include <QtLocation/private/qdeclarativegeoroutemodel_p.h>
42 
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 <QtPositioning/QGeoRectangle>
49 
50 QT_BEGIN_NAMESPACE
51 
52 /*!
53     \qmltype Route
54     \instantiates QDeclarativeGeoRoute
55     \inqmlmodule QtLocation
56     \ingroup qml-QtLocation5-routing
57     \since QtLocation 5.5
58 
59     \brief The Route type represents one geographical route.
60 
61     A Route type contains high level information about a route, such
62     as the length the route, the estimated travel time for the route,
63     and enough information to render a basic image of the route on a map.
64 
65     The QGeoRoute object also contains a list of \l RouteSegment objects which
66     describe subsections of the route in greater detail.
67 
68     The primary means of acquiring Route objects is \l RouteModel.
69 
70     \section1 Example
71 
72     This example shows how to display a route's maneuvers in a ListView:
73 
74     \snippet declarative/routing.qml QtQuick import
75     \snippet declarative/maps.qml QtLocation import
76     \codeline
77     \snippet declarative/routing.qml Route Maneuver List1
78     \snippet declarative/routing.qml Route Maneuver List2
79     \snippet declarative/routing.qml Route Maneuver List3
80 
81 */
82 
QDeclarativeGeoRoute(QObject * parent)83 QDeclarativeGeoRoute::QDeclarativeGeoRoute(QObject *parent)
84     : QObject(parent)
85 {
86 }
87 
QDeclarativeGeoRoute(const QGeoRoute & route,QObject * parent)88 QDeclarativeGeoRoute::QDeclarativeGeoRoute(const QGeoRoute &route, QObject *parent)
89     : QObject(parent), route_(route)
90 {
91 }
92 
~QDeclarativeGeoRoute()93 QDeclarativeGeoRoute::~QDeclarativeGeoRoute() {}
94 
initSegments(unsigned int lastIndex)95 void QDeclarativeGeoRoute::initSegments(unsigned int lastIndex) // -1  turns it into unsigned int max
96 {
97     if (!segmentsDirty_)
98         return;
99 
100     const bool isLeg = qobject_cast<QDeclarativeGeoRoute *>(parent());
101     QGeoRouteSegment segment = route_.firstRouteSegment();
102     unsigned int idx = 0;
103     unsigned int initialListSize = static_cast<unsigned int>(segments_.size());
104     while (segment.isValid()) {
105         if (idx >= initialListSize) {
106             QDeclarativeGeoRouteSegment *routeSegment = new QDeclarativeGeoRouteSegment(segment, this);
107             QQmlEngine::setContextForObject(routeSegment, QQmlEngine::contextForObject(this));
108             segments_.append(routeSegment);
109         }
110         if (isLeg && segment.isLegLastSegment()) {
111             segmentsDirty_ = false;
112             return;
113         }
114         ++idx;
115         segment = segment.nextRouteSegment();
116         if (idx > lastIndex && segment.isValid()) // Do not clean segmentsDirty_ if there are still segments to initialize
117             return;
118     }
119     segmentsDirty_ = false;
120 }
121 
122 /*!
123     \internal
124 */
routePath()125 QList<QGeoCoordinate> QDeclarativeGeoRoute::routePath()
126 {
127     return route_.path();
128 }
129 
130 /*!
131     \qmlproperty georectangle QtLocation::Route::bounds
132 
133     Read-only property which holds a bounding box which encompasses the entire route.
134 
135 */
136 
bounds() const137 QGeoRectangle QDeclarativeGeoRoute::bounds() const
138 {
139     return route_.bounds();
140 }
141 
142 /*!
143     \qmlproperty int QtLocation::Route::travelTime
144 
145     Read-only property which holds the estimated amount of time it will take to
146     traverse this route, in seconds.
147 
148 */
149 
travelTime() const150 int QDeclarativeGeoRoute::travelTime() const
151 {
152     return route_.travelTime();
153 }
154 
155 /*!
156     \qmlproperty real QtLocation::Route::distance
157 
158     Read-only property which holds distance covered by this route, in meters.
159 */
160 
distance() const161 qreal QDeclarativeGeoRoute::distance() const
162 {
163     return route_.distance();
164 }
165 
166 /*!
167     \qmlproperty list<coordinate> QtLocation::Route::path
168 
169     Read-only property which holds the geographical coordinates of this route.
170     Coordinates are listed in the order in which they would be traversed by someone
171     traveling along this segment of the route.
172 
173     To access individual segments you can use standard list accessors: 'path.length'
174     indicates the number of objects and 'path[index starting from zero]' gives
175     the actual object.
176 
177     \sa QtPositioning::coordinate
178 */
179 
path() const180 QJSValue QDeclarativeGeoRoute::path() const
181 {
182     QQmlContext *context = QQmlEngine::contextForObject(parent());
183     QQmlEngine *engine = context->engine();
184     QV4::ExecutionEngine *v4 = QQmlEnginePrivate::getV4Engine(engine);
185 
186     QV4::Scope scope(v4);
187     QV4::Scoped<QV4::ArrayObject> pathArray(scope, v4->newArrayObject(route_.path().length()));
188     for (int i = 0; i < route_.path().length(); ++i) {
189         const QGeoCoordinate &c = route_.path().at(i);
190 
191         QV4::ScopedValue cv(scope, v4->fromVariant(QVariant::fromValue(c)));
192         pathArray->put(i, cv);
193     }
194 
195     return QJSValue(v4, pathArray.asReturnedValue());
196 }
197 
setPath(const QJSValue & value)198 void QDeclarativeGeoRoute::setPath(const QJSValue &value)
199 {
200     if (!value.isArray())
201         return;
202 
203     QList<QGeoCoordinate> pathList;
204     quint32 length = value.property(QStringLiteral("length")).toUInt();
205     for (quint32 i = 0; i < length; ++i) {
206         bool ok;
207         QGeoCoordinate c = parseCoordinate(value.property(i), &ok);
208 
209         if (!ok || !c.isValid()) {
210             qmlWarning(this) << "Unsupported path type";
211             return;
212         }
213 
214         pathList.append(c);
215     }
216 
217     if (route_.path() == pathList)
218         return;
219 
220     route_.setPath(pathList);
221 
222     emit pathChanged();
223 }
224 
225 /*!
226     \qmlproperty list<RouteSegment> QtLocation::Route::segments
227 
228     Read-only property which holds the list of \l RouteSegment objects of this route.
229 
230     To access individual segments you can use standard list accessors: 'segments.length'
231     indicates the number of objects and 'segments[index starting from zero]' gives
232     the actual objects.
233 
234     \sa RouteSegment
235 */
236 
segments()237 QQmlListProperty<QDeclarativeGeoRouteSegment> QDeclarativeGeoRoute::segments()
238 {
239     return QQmlListProperty<QDeclarativeGeoRouteSegment>(this, 0, segments_append, segments_count,
240                                                          segments_at, segments_clear);
241 }
242 
243 /*!
244     \internal
245 */
segments_append(QQmlListProperty<QDeclarativeGeoRouteSegment> * prop,QDeclarativeGeoRouteSegment * segment)246 void QDeclarativeGeoRoute::segments_append(QQmlListProperty<QDeclarativeGeoRouteSegment> *prop,
247                                            QDeclarativeGeoRouteSegment *segment)
248 {
249     QDeclarativeGeoRoute *declRoute = static_cast<QDeclarativeGeoRoute *>(prop->object);
250     declRoute->initSegments();
251     declRoute->appendSegment(segment);
252 }
253 
254 /*!
255     \internal
256 */
segments_count(QQmlListProperty<QDeclarativeGeoRouteSegment> * prop)257 int QDeclarativeGeoRoute::segments_count(QQmlListProperty<QDeclarativeGeoRouteSegment> *prop)
258 {
259     QDeclarativeGeoRoute *declRoute = static_cast<QDeclarativeGeoRoute *>(prop->object);
260     return declRoute->segmentsCount();
261 }
262 
263 /*!
264     \internal
265 */
segments_at(QQmlListProperty<QDeclarativeGeoRouteSegment> * prop,int index)266 QDeclarativeGeoRouteSegment *QDeclarativeGeoRoute::segments_at(QQmlListProperty<QDeclarativeGeoRouteSegment> *prop, int index)
267 {
268     QDeclarativeGeoRoute *declRoute = static_cast<QDeclarativeGeoRoute *>(prop->object);
269     declRoute->initSegments(index); // init only what's needed.
270     return declRoute->segments_.at(index);
271 }
272 
273 /*!
274     \internal
275 */
segments_clear(QQmlListProperty<QDeclarativeGeoRouteSegment> * prop)276 void QDeclarativeGeoRoute::segments_clear(QQmlListProperty<QDeclarativeGeoRouteSegment> *prop)
277 {
278     static_cast<QDeclarativeGeoRoute *>(prop->object)->clearSegments();
279 }
280 
281 /*!
282     \internal
283 */
appendSegment(QDeclarativeGeoRouteSegment * segment)284 void QDeclarativeGeoRoute::appendSegment(QDeclarativeGeoRouteSegment *segment)
285 {
286     segments_.append(segment);
287 }
288 
289 /*!
290     \internal
291 */
clearSegments()292 void QDeclarativeGeoRoute::clearSegments()
293 {
294     segments_.clear();
295 }
296 
297 /*!
298     \qmlmethod int QtLocation::Route::segmentsCount()
299 
300     Returns the number of segments in the route
301 
302     \sa RouteSegment
303 
304     \since 5.11
305 */
306 
segmentsCount() const307 int QDeclarativeGeoRoute::segmentsCount() const
308 {
309     return qMax(route_.d_ptr->segmentsCount(), segments_.count());
310 }
311 
route() const312 const QGeoRoute &QDeclarativeGeoRoute::route() const
313 {
314     return route_;
315 }
316 
317 /*!
318     \qmlproperty RouteQuery QtLocation::Route::routeQuery
319 
320     Returns the route query associated with this route.
321 
322     \since 5.11
323 */
routeQuery()324 QDeclarativeGeoRouteQuery *QDeclarativeGeoRoute::routeQuery()
325 {
326     if (!routeQuery_)
327         routeQuery_ = new QDeclarativeGeoRouteQuery(route_.request(), this);
328     return routeQuery_;
329 }
330 
331 /*!
332     \qmlproperty list<Route> QtLocation::Route::legs
333 
334     Returns the route legs associated with this route.
335     Route legs are the sub-routes between each two adjacent waypoints.
336     The result may be empty, if this level of detail is not supported by the
337     backend.
338 
339     \since QtLocation 5.12
340 */
legs()341 QList<QObject *> QDeclarativeGeoRoute::legs()
342 {
343     // route_.routeLegs() is expected not to change.
344     // The following if condition is expected to be run only once.
345     if (route_.routeLegs().size() != legs_.size()) {
346         legs_.clear();
347         QList<QGeoRouteLeg> rlegs = route_.routeLegs();
348         for (const QGeoRouteLeg &r: rlegs) {
349             QDeclarativeGeoRouteLeg *dr = new QDeclarativeGeoRouteLeg(r, this);
350             legs_.append(dr);
351         }
352     }
353     return legs_;
354 }
355 
356 /*!
357     \qmlproperty Object Route::extendedAttributes
358 
359     This property holds the extended attributes of the route and is a map.
360     These attributes are plugin specific, and can be empty.
361 
362     Consult the \l {Qt Location#Plugin References and Parameters}{plugin documentation}
363     for what attributes are supported and how they should be used.
364 
365     Note, due to limitations of the QQmlPropertyMap, it is not possible
366     to declaratively specify the attributes in QML, assignment of attributes keys
367     and values can only be accomplished by JavaScript.
368 
369     \since QtLocation 5.13
370 */
extendedAttributes() const371 QQmlPropertyMap *QDeclarativeGeoRoute::extendedAttributes() const
372 {
373     if (!m_extendedAttributes) {
374         QDeclarativeGeoRoute *self = const_cast<QDeclarativeGeoRoute *>(this);
375         self->m_extendedAttributes = new QQmlPropertyMap(self);
376         // Fill it
377         const QVariantMap &xAttrs = route_.extendedAttributes();
378         const QStringList &keys = xAttrs.keys();
379         for (const QString &key: keys)
380             self->m_extendedAttributes->insert(key, xAttrs.value(key));
381     }
382     return m_extendedAttributes;
383 }
384 
385 /*!
386     \qmlmethod bool QtLocation::Route::equals(Route other)
387 
388     This method performs deep comparison if the present route
389     is identical to the \a other route.
390     Returns \c true if the routes are equal.
391 
392     \since 5.12
393 */
equals(QDeclarativeGeoRoute * other) const394 bool QDeclarativeGeoRoute::equals(QDeclarativeGeoRoute *other) const
395 {
396     return route_ == other->route_;
397 }
398 
399 /*!
400     \qmltype RouteLeg
401     \instantiates QDeclarativeGeoRouteLeg
402     \inqmlmodule QtLocation
403     \ingroup qml-QtLocation5-routing
404     \since QtLocation 5.12
405 
406     \brief The RouteLeg type represents a leg of a Route, that is the portion
407     of a route between one waypoint and the next.
408 
409     \note Since RouteLeg is a subclass of Route, QtLocation::Route::legs will
410     return an empty list if accessed on a route leg.
411 */
412 
413 /*!
414     \qmlproperty int QtLocation::RouteLeg::legIndex
415 
416     Read-only property which holds the index of the leg within the containing Route's list of QtLocation::Route::legs .
417 */
418 
419 /*!
420     \qmlproperty Route QtLocation::RouteLeg::overallRoute
421 
422     Read-only property which holds the Route that contains this leg.
423 */
424 
425 
QDeclarativeGeoRouteLeg(QObject * parent)426 QDeclarativeGeoRouteLeg::QDeclarativeGeoRouteLeg(QObject *parent)
427     : QDeclarativeGeoRoute(parent)
428 {
429 
430 }
431 
QDeclarativeGeoRouteLeg(const QGeoRouteLeg & routeLeg,QObject * parent)432 QDeclarativeGeoRouteLeg::QDeclarativeGeoRouteLeg(const QGeoRouteLeg &routeLeg, QObject *parent)
433     : QDeclarativeGeoRoute(routeLeg, parent), m_routeLeg(routeLeg)
434 {
435 
436 }
437 
~QDeclarativeGeoRouteLeg()438 QDeclarativeGeoRouteLeg::~QDeclarativeGeoRouteLeg()
439 {
440 
441 }
442 
legIndex() const443 int QDeclarativeGeoRouteLeg::legIndex() const
444 {
445     return m_routeLeg.legIndex();
446 }
447 
overallRoute() const448 QObject *QDeclarativeGeoRouteLeg::overallRoute() const
449 {
450     QDeclarativeGeoRoute *containingRoute = qobject_cast<QDeclarativeGeoRoute *>(parent());
451     if (Q_UNLIKELY(!containingRoute))
452         return new QDeclarativeGeoRoute(m_routeLeg.overallRoute(), parent());
453     return containingRoute;
454 }
455 
456 QT_END_NAMESPACE
457