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 "qgeoroutexmlparser.h"
38 
39 #include <QXmlStreamReader>
40 #include <QStringList>
41 #include <QString>
42 #include <QtCore/QThreadPool>
43 #include <QDebug>
44 
45 #include <QtPositioning/QGeoRectangle>
46 #include <QtPositioning/QGeoPath>
47 #include <QtLocation/QGeoRoute>
48 #include <QtLocation/private/qgeoroutesegment_p.h>
49 
50 QT_BEGIN_NAMESPACE
51 
QGeoDynamicSpeedInfoContainer()52 QGeoDynamicSpeedInfoContainer::QGeoDynamicSpeedInfoContainer()
53 : trafficSpeed(0)
54 , baseSpeed(0)
55 , trafficTime(0)
56 , baseTime(0)
57 {}
58 
QGeoRouteXmlParser(const QGeoRouteRequest & request)59 QGeoRouteXmlParser::QGeoRouteXmlParser(const QGeoRouteRequest &request)
60         : m_request(request)
61 {
62 }
63 
~QGeoRouteXmlParser()64 QGeoRouteXmlParser::~QGeoRouteXmlParser()
65 {
66 }
67 
parse(const QByteArray & data)68 void QGeoRouteXmlParser::parse(const QByteArray &data)
69 {
70     m_data = data;
71 //    QFile file("/tmp/here.xml");
72 //    file.open(QIODevice::WriteOnly);
73 //    file.write(data);
74 //    file.close();
75     QThreadPool::globalInstance()->start(this);
76 }
77 
run()78 void QGeoRouteXmlParser::run()
79 {
80     m_reader = new QXmlStreamReader(m_data);
81 
82     if (!parseRootElement())
83         emit error(m_reader->errorString());
84     else
85         emit results(m_results);
86 
87     delete m_reader;
88     m_reader = 0;
89 }
90 
parseRootElement()91 bool QGeoRouteXmlParser::parseRootElement()
92 {
93     if (!m_reader->readNextStartElement()) {
94         m_reader->raiseError("Expected a root element named \"CalculateRoute\" (no root element found).");
95         return false;
96     }
97 
98     if (m_reader->name() == QLatin1String("Error")) {
99         QXmlStreamAttributes attributes = m_reader->attributes();
100         if (attributes.value(QStringLiteral("type")) == QLatin1String("ApplicationError")
101             && attributes.value("subtype") == QLatin1String("NoRouteFound"))
102             return true;
103     }
104 
105     bool updateroute = false;
106     if (m_reader->name() != "CalculateRoute" && m_reader->name() != "GetRoute")  {
107         m_reader->raiseError(QString("The root element is expected to have the name \"CalculateRoute\" or \"GetRoute\" (root element was named \"%1\").").arg(m_reader->name().toString()));
108         return false;
109     } else if (m_reader->name() == "GetRoute") {
110         updateroute = true;
111     }
112 
113     if (m_reader->readNextStartElement()) {
114         if (m_reader->name() != "Response") {
115             m_reader->raiseError(QString("Expected a element named \"Response\" (element was named \"%1\").").arg(m_reader->name().toString()));
116             return false;
117         }
118     }
119 
120     while (m_reader->readNextStartElement() && !m_reader->hasError()) {
121         if (m_reader->name() == "Route") {
122             QGeoRoute route;
123             route.setRequest(m_request);
124             if (updateroute)
125                 route.setTravelMode(QGeoRouteRequest::TravelMode(int(m_request.travelModes())));
126             if (!parseRoute(&route))
127                 continue; //route parsing failed move on to the next
128             m_results.append(route);
129         } else if (m_reader->name() == "Progress") {
130             //TODO: updated route progress
131             m_reader->skipCurrentElement();
132         } else {
133             m_reader->skipCurrentElement();
134         }
135     }
136 
137     return !m_reader->hasError();
138 }
139 
parseRoute(QGeoRoute * route)140 bool QGeoRouteXmlParser::parseRoute(QGeoRoute *route)
141 {
142     Q_ASSERT(m_reader->isStartElement() && m_reader->name() == "Route");
143     m_maneuvers.clear();
144 //    m_segments.clear();
145     m_legs.clear();
146     int legIndex = 0;
147     m_reader->readNext();
148     while (!(m_reader->tokenType() == QXmlStreamReader::EndElement && m_reader->name() == "Route") &&
149            !m_reader->hasError()) {
150         if (m_reader->tokenType() == QXmlStreamReader::StartElement) {
151             if (m_reader->name() == "RouteId") {
152                 route->setRouteId(m_reader->readElementText());
153             }
154             //else if (m_reader->name() == "Waypoint") {
155             //    succeeded = parseWaypoint(route);
156             //}
157             else if (m_reader->name() == "Mode") {
158                 if (!parseMode(route))
159                     return false;
160             } else if (m_reader->name() == "Shape") {
161                 QString elementName = m_reader->name().toString();
162                 QList<QGeoCoordinate> path;
163                 if (!parseGeoPoints(m_reader->readElementText(), &path, elementName))
164                     return false;
165                 route->setPath(path);
166             } else if (m_reader->name() == "BoundingBox") {
167                 QGeoRectangle bounds;
168                 if (!parseBoundingBox(bounds))
169                     return false;
170                 route->setBounds(bounds);
171             } else if (m_reader->name() == "Leg") {
172                 if (!parseLeg(legIndex++))
173                     return false;
174             } else if (m_reader->name() == "Summary") {
175                 if (!parseSummary(route))
176                     return false;
177             } else {
178                 m_reader->skipCurrentElement();
179             }
180         }
181         m_reader->readNext();
182     }
183 
184     if (m_reader->hasError())
185         return false;
186 
187     return postProcessRoute(route);
188 }
189 
parseLeg(int legIndex)190 bool QGeoRouteXmlParser::parseLeg(int legIndex)
191 {
192     Q_ASSERT(m_reader->isStartElement() && m_reader->name() == QStringLiteral("Leg"));
193     QGeoRouteLeg leg;
194     leg.setLegIndex(legIndex);
195     m_reader->readNext();
196     QList<QGeoManeuverContainer> maneuvers;
197     QList<QGeoRouteSegmentContainer> links;
198     while (!(m_reader->tokenType() == QXmlStreamReader::EndElement
199              && m_reader->name() == QStringLiteral("Leg")) &&
200            !m_reader->hasError()) {
201         if (m_reader->tokenType() == QXmlStreamReader::StartElement) {
202             if (m_reader->name() == QStringLiteral("Maneuver")) {
203                 if (!parseManeuver(maneuvers))
204                     return false;
205             }
206 // Currently unused, after requesting shape attribute in maneuvers.
207 // Links, however, contain additional info, such as speed limits, and might become needed in the future.
208 //            else if (m_reader->name() == QStringLiteral("Link")) {
209 //                if (!parseLink(links))
210 //                    return false;
211 //            }
212             else if (m_reader->name() == "TravelTime") {
213                 leg.setTravelTime(qRound(m_reader->readElementText().toDouble()));
214             } else if (m_reader->name() == "Length") {
215                 leg.setDistance(m_reader->readElementText().toDouble());
216             } else {
217                 m_reader->skipCurrentElement();
218             }
219         }
220         m_reader->readNext();
221     }
222 
223     if (m_reader->hasError())
224         return false;
225 
226     m_legs << leg;
227 //    m_segments << links;
228     m_maneuvers << maneuvers;
229     return true;
230 }
231 
232 //static bool fuzzyCompare(const QGeoCoordinate &a, const QGeoCoordinate& b)
233 //{
234 //    return qFuzzyCompare(a.latitude(), b.latitude()) && qFuzzyCompare(a.longitude(), b.longitude());
235 //}
236 
postProcessRoute(QGeoRoute * route)237 bool QGeoRouteXmlParser::postProcessRoute(QGeoRoute *route)
238 {
239     QList<QList<QGeoRouteSegment>> legSegments;
240     Q_ASSERT(m_maneuvers.size());
241 
242 
243     // Step 3: populate the linkMap, linkId -> linkContainer
244     for (int i = 0; i < m_maneuvers.size(); i++) {
245         legSegments << QList<QGeoRouteSegment>();
246         QList<QGeoRouteSegment> &segments = legSegments[i];
247         QList<QGeoManeuverContainer> &maneuvers = m_maneuvers[i];
248         for (int j = 0; j < m_maneuvers.at(i).size(); j++) {
249             QGeoManeuverContainer &maneuver = maneuvers[j];
250             QGeoRouteSegment segment;
251 
252             QVariantMap extendedAttributes;
253             extendedAttributes["first"] = maneuver.first;
254             extendedAttributes["last"] = maneuver.last;
255             extendedAttributes["legIndex"] = i;
256             extendedAttributes["id"] = maneuver.id;
257             extendedAttributes["toLink"] = maneuver.toLink;
258             extendedAttributes["index"] = j;
259             maneuver.maneuver.setExtendedAttributes(extendedAttributes);
260 
261             segment.setDistance(maneuver.maneuver.distanceToNextInstruction());
262             segment.setTravelTime(maneuver.maneuver.timeToNextInstruction());
263             segment.setPath(maneuver.path);
264             segment.setManeuver(maneuver.maneuver);
265             segments << segment;
266         }
267     }
268 
269     // Step 7: connect all segments.
270     QGeoRouteSegment segment;
271     QGeoRouteSegment firstSegment;
272     for (auto &segments: legSegments) {
273         for (int j = 0; j < segments.size(); j++) {
274             if (segment.isValid()) {
275                 segment.setNextRouteSegment(segments[j]);
276             } else {
277                 firstSegment = segments[j];
278             }
279             segment = segments[j];
280             if (j == segments.size() - 1) {
281                 QGeoRouteSegmentPrivate *sp = QGeoRouteSegmentPrivate::get(segment);
282                 sp->setLegLastSegment(true);
283             }
284         }
285     }
286 
287     if (firstSegment.isValid())
288         route->setFirstRouteSegment(firstSegment);
289 
290     // Step 8: fill route legs.
291     for (int i = 0; i < m_legs.size(); i++) {
292         m_legs[i].setTravelMode(route->travelMode());
293         m_legs[i].setRequest(route->request());
294         m_legs[i].setOverallRoute(*route);
295         m_legs[i].setLegIndex(i);
296 
297         m_legs[i].setFirstRouteSegment(legSegments[i].first());
298 
299         // handle path
300         QList<QGeoCoordinate> path;
301         QGeoRouteSegment s = m_legs[i].firstRouteSegment();
302         while (s.isValid()) {
303             path.append(s.path());
304             if (s.isLegLastSegment())
305                 break;
306             s = s.nextRouteSegment();
307         }
308         m_legs[i].setPath(path);
309         m_legs[i].setBounds(QGeoPath(path).boundingGeoRectangle());
310     }
311     route->setRouteLegs(m_legs);
312     m_legs.clear();
313 //    m_segments.clear();
314     m_maneuvers.clear();
315     return true;
316 }
317 
318 /*
319 bool QGeoRouteXmlParser::parseWaypoint(QGeoRoute *route)
320 {
321     Q_ASSERT(m_reader->isStartElement() && m_reader->name() == "Waypoint");
322     m_reader->readNext();
323     QList<QGeoCoordinate> path(route->pathSummary());
324 
325     while (!(m_reader->tokenType() == QXmlStreamReader::EndElement && m_reader->name() == "Waypoint")) {
326         if (m_reader->tokenType() == QXmlStreamReader::StartElement) {
327             if (m_reader->name() == "MappedPosition") {
328                 QGeoCoordinate coordinates;
329                 if(!parseCoordinates(coordinates))
330                     return false;
331                 path.append(coordinates);
332             }
333             else {
334                 m_reader->skipCurrentElement();
335             }
336         }
337         m_reader->readNext();
338     }
339     route->setPathSummary(path);
340     return true;
341 }
342 */
343 
parseMode(QGeoRoute * route)344 bool QGeoRouteXmlParser::parseMode(QGeoRoute *route)
345 {
346     Q_ASSERT(m_reader->isStartElement() && m_reader->name() == "Mode");
347     m_reader->readNext();
348 
349     while (!(m_reader->tokenType() == QXmlStreamReader::EndElement && m_reader->name() == "Mode") &&
350            !m_reader->hasError()) {
351         if (m_reader->tokenType() == QXmlStreamReader::StartElement) {
352             if (m_reader->name() == "TransportModes") {
353                 QString value = m_reader->readElementText();
354                 if (value == "car")
355                     route->setTravelMode(QGeoRouteRequest::CarTravel);
356                 else if (value == "pedestrian")
357                     route->setTravelMode(QGeoRouteRequest::PedestrianTravel);
358                 else if (value == "publicTransport")
359                     route->setTravelMode(QGeoRouteRequest::PublicTransitTravel);
360                 else if (value == "bicycle")
361                     route->setTravelMode(QGeoRouteRequest::BicycleTravel);
362                 else if (value == "truck")
363                     route->setTravelMode(QGeoRouteRequest::TruckTravel);
364                 else {
365                     // unsupported mode
366                     m_reader->raiseError(QString("Unsupported travel mode '\"%1\"'").arg(value));
367                     return false;
368                 }
369             } else {
370                 m_reader->skipCurrentElement();
371             }
372         }
373         m_reader->readNext();
374     }
375     return !m_reader->hasError();
376 }
377 
parseSummary(QGeoRoute * route)378 bool QGeoRouteXmlParser::parseSummary(QGeoRoute *route)
379 {
380     Q_ASSERT(route);
381     Q_ASSERT(m_reader->isStartElement() && m_reader->name() == "Summary");
382     m_reader->readNext();
383 
384     double baseTime = -1, trafficTime = -1;
385 
386     while (!(m_reader->tokenType() == QXmlStreamReader::EndElement && m_reader->name() == "Summary") &&
387            !m_reader->hasError()) {
388         if (m_reader->tokenType() == QXmlStreamReader::StartElement) {
389             if (m_reader->name() == "Distance") {
390                 route->setDistance(m_reader->readElementText().toDouble());
391             } else if (m_reader->name() == "TrafficTime") {
392                 trafficTime = m_reader->readElementText().toDouble();
393             } else if (m_reader->name() == "BaseTime") {
394                 baseTime = m_reader->readElementText().toDouble();
395             } else {
396                 m_reader->skipCurrentElement();
397             }
398         }
399         m_reader->readNext();
400     }
401 
402     if (m_reader->hasError())
403         return false;
404 
405     if (trafficTime >= 0)
406         route->setTravelTime(trafficTime);
407     else if (baseTime >= 0)
408         route->setTravelTime(baseTime);
409 
410     return true;
411 }
412 
parseCoordinates(QGeoCoordinate & coord)413 bool QGeoRouteXmlParser::parseCoordinates(QGeoCoordinate &coord)
414 {
415     QString currentElement = m_reader->name().toString();
416     m_reader->readNext();
417 
418     while (!(m_reader->tokenType() == QXmlStreamReader::EndElement && m_reader->name() == currentElement)  &&
419            !m_reader->hasError()) {
420         if (m_reader->tokenType() == QXmlStreamReader::StartElement) {
421             QString name = m_reader->name().toString();
422             QString value = m_reader->readElementText();
423             if (name == "Latitude")
424                 coord.setLatitude(value.toDouble());
425             else if (name == "Longitude")
426                 coord.setLongitude(value.toDouble());
427         }
428         m_reader->readNext();
429     }
430 
431     return !m_reader->hasError();
432 }
433 
parseManeuver(QList<QGeoManeuverContainer> & maneuvers)434 bool QGeoRouteXmlParser::parseManeuver(QList<QGeoManeuverContainer> &maneuvers)
435 {
436     Q_ASSERT(m_reader->isStartElement() && m_reader->name() == "Maneuver");
437 
438     if (!m_reader->attributes().hasAttribute("id")) {
439         m_reader->raiseError("The element \"Maneuver\" did not have the required attribute \"id\".");
440         return false;
441     }
442     QGeoManeuverContainer maneuverContainter;
443     maneuverContainter.id = m_reader->attributes().value("id").toString();
444 
445     m_reader->readNext();
446     while (!(m_reader->tokenType() == QXmlStreamReader::EndElement && m_reader->name() == "Maneuver") &&
447            !m_reader->hasError()) {
448         if (m_reader->tokenType() == QXmlStreamReader::StartElement) {
449             if (m_reader->name() == "Position") {
450                 QGeoCoordinate coordinates;
451                 if (parseCoordinates(coordinates))
452                     maneuverContainter.maneuver.setPosition(coordinates);
453             } else if (m_reader->name() == "Instruction") {
454                 maneuverContainter.maneuver.setInstructionText(m_reader->readElementText());
455             } else if (m_reader->name() == "Shape") {
456                 QString elementName = m_reader->name().toString();
457                 QList<QGeoCoordinate> path;
458                 if (!parseGeoPoints(m_reader->readElementText(), &path, elementName))
459                     return false;
460                 maneuverContainter.path = path;
461             } else if (m_reader->name() == "ToLink") {
462                 maneuverContainter.toLink = m_reader->readElementText();
463             } else if (m_reader->name() == "TravelTime") {
464                 maneuverContainter.maneuver.setTimeToNextInstruction(qRound(m_reader->readElementText().toDouble()));
465             } else if (m_reader->name() == "Length") {
466                 maneuverContainter.maneuver.setDistanceToNextInstruction(m_reader->readElementText().toDouble());
467             } else if (m_reader->name() == "Direction") {
468                 QString value = m_reader->readElementText();
469                 if (value == "forward")
470                     maneuverContainter.maneuver.setDirection(QGeoManeuver::DirectionForward);
471                 else if (value == "bearRight")
472                     maneuverContainter.maneuver.setDirection(QGeoManeuver::DirectionBearRight);
473                 else if (value == "lightRight")
474                     maneuverContainter.maneuver.setDirection(QGeoManeuver::DirectionLightRight);
475                 else if (value == "right")
476                     maneuverContainter.maneuver.setDirection(QGeoManeuver::DirectionRight);
477                 else if (value == "hardRight")
478                     maneuverContainter.maneuver.setDirection(QGeoManeuver::DirectionHardRight);
479                 else if (value == "uTurnRight")
480                     maneuverContainter.maneuver.setDirection(QGeoManeuver::DirectionUTurnRight);
481                 else if (value == "uTurnLeft")
482                     maneuverContainter.maneuver.setDirection(QGeoManeuver::DirectionUTurnLeft);
483                 else if (value == "hardLeft")
484                     maneuverContainter.maneuver.setDirection(QGeoManeuver::DirectionHardLeft);
485                 else if (value == "left")
486                     maneuverContainter.maneuver.setDirection(QGeoManeuver::DirectionLeft);
487                 else if (value == "lightLeft")
488                     maneuverContainter.maneuver.setDirection(QGeoManeuver::DirectionLightLeft);
489                 else if (value == "bearLeft")
490                     maneuverContainter.maneuver.setDirection(QGeoManeuver::DirectionBearLeft);
491                 else
492                     maneuverContainter.maneuver.setDirection(QGeoManeuver::NoDirection);
493             } else {
494                 m_reader->skipCurrentElement();
495             }
496         }
497         m_reader->readNext();
498     }
499 
500     if (m_reader->hasError())
501         return false;
502 
503     maneuvers.append(maneuverContainter);
504     return true;
505 }
506 
parseLink(QList<QGeoRouteSegmentContainer> & links)507 bool QGeoRouteXmlParser::parseLink(QList<QGeoRouteSegmentContainer> &links)
508 {
509     Q_ASSERT(m_reader->isStartElement() && m_reader->name() == QStringLiteral("Link"));
510     m_reader->readNext();
511 
512     QGeoRouteSegmentContainer segmentContainer;
513 
514     while (!(m_reader->tokenType() == QXmlStreamReader::EndElement && m_reader->name() == QStringLiteral("Link")) &&
515            !m_reader->hasError()) {
516         if (m_reader->tokenType() == QXmlStreamReader::StartElement) {
517             if (m_reader->name() == QStringLiteral("LinkId")) {
518                 segmentContainer.id = m_reader->readElementText();
519             } else if (m_reader->name() == QStringLiteral("Shape")) {
520                 QString elementName = m_reader->name().toString();
521                 QList<QGeoCoordinate> path;
522                 parseGeoPoints(m_reader->readElementText(), &path, elementName);
523                 segmentContainer.segment.setPath(path);
524             } else if (m_reader->name() == QStringLiteral("Length")) {
525                 segmentContainer.segment.setDistance(m_reader->readElementText().toDouble());
526             } else if (m_reader->name() == QStringLiteral("Maneuver")) {
527                 segmentContainer.maneuverId = m_reader->readElementText();
528             } else if (m_reader->name() == QStringLiteral("DynamicSpeedInfo")) {
529                 QGeoDynamicSpeedInfoContainer speedInfo;
530                 if (!parseDynamicSpeedInfo(speedInfo))
531                     return false;
532                 const double time = speedInfo.trafficTime >= 0 ? speedInfo.trafficTime : speedInfo.baseTime;
533                 if (time >= 0)
534                     segmentContainer.segment.setTravelTime(time);
535             } else {
536                 m_reader->skipCurrentElement();
537             }
538         }
539         m_reader->readNext();
540     }
541 
542     if (m_reader->hasError())
543         return false;
544     links.append(segmentContainer);
545     return true;
546 }
547 
parseGeoPoints(const QString & strPoints,QList<QGeoCoordinate> * geoPoints,const QString & elementName)548 bool QGeoRouteXmlParser::parseGeoPoints(const QString &strPoints, QList<QGeoCoordinate> *geoPoints, const QString &elementName)
549 {
550     QStringList rawPoints = strPoints.split(' ');
551 
552     for (int i = 0; i < rawPoints.length(); ++i) {
553         QStringList coords = rawPoints[i].split(',');
554 
555         if (coords.length() != 2) {
556             m_reader->raiseError(QString("Each of the space separated values of \"%1\" is expected to be a comma separated pair of coordinates (value was \"%2\")").arg(elementName).arg(rawPoints[i]));
557             return false;
558         }
559 
560         bool ok = false;
561         QString latString = coords[0];
562         double lat = latString.toDouble(&ok);
563 
564         if (!ok) {
565             m_reader->raiseError(QString("The latitude portions of \"%1\" are expected to have a value convertable to a double (value was \"%2\")").arg(elementName).arg(latString));
566             return false;
567         }
568 
569         QString lngString = coords[1];
570         double lng = lngString.toDouble(&ok);
571 
572         if (!ok) {
573             m_reader->raiseError(QString("The longitude portions of \"%1\" are expected to have a value convertable to a double (value was \"%2\")").arg(elementName).arg(lngString));
574             return false;
575         }
576 
577         QGeoCoordinate geoPoint(lat, lng);
578         geoPoints->append(geoPoint);
579     }
580 
581     return true;
582 }
583 
parseBoundingBox(QGeoRectangle & bounds)584 bool QGeoRouteXmlParser::parseBoundingBox(QGeoRectangle &bounds)
585 {
586     Q_ASSERT(m_reader->isStartElement() && m_reader->name() == "BoundingBox");
587 
588     QGeoCoordinate tl;
589     QGeoCoordinate br;
590 
591     m_reader->readNext();
592     while (!(m_reader->tokenType() == QXmlStreamReader::EndElement && m_reader->name() == "BoundingBox") &&
593            !m_reader->hasError()) {
594         if (m_reader->tokenType() == QXmlStreamReader::StartElement) {
595             if (m_reader->name() == "TopLeft") {
596                 QGeoCoordinate coordinates;
597                 if (parseCoordinates(coordinates))
598                     tl = coordinates;
599             } else if (m_reader->name() == "BottomRight") {
600                 QGeoCoordinate coordinates;
601                 if (parseCoordinates(coordinates))
602                     br = coordinates;
603             } else {
604                 m_reader->skipCurrentElement();
605             }
606         }
607         m_reader->readNext();
608     }
609 
610     if (m_reader->hasError())
611         return false;
612 
613     if (tl.isValid() && br.isValid()) {
614         bounds = QGeoRectangle(tl, br);
615         return true;
616     }
617 
618     return false;
619 }
620 
parseDynamicSpeedInfo(QGeoDynamicSpeedInfoContainer & speedInfo)621 bool QGeoRouteXmlParser::parseDynamicSpeedInfo(QGeoDynamicSpeedInfoContainer &speedInfo)
622 {
623     Q_ASSERT(m_reader->isStartElement() && m_reader->name() == QStringLiteral("DynamicSpeedInfo"));
624 
625     m_reader->readNext();
626     while (!(m_reader->tokenType() == QXmlStreamReader::EndElement && m_reader->name() == QStringLiteral("DynamicSpeedInfo")) &&
627            !m_reader->hasError()) {
628         if (m_reader->tokenType() == QXmlStreamReader::StartElement) {
629             if (m_reader->name() == QStringLiteral("TrafficSpeed")) {
630                 speedInfo.trafficSpeed = m_reader->readElementText().toDouble();
631             } else if (m_reader->name() == QStringLiteral("TrafficTime")) {
632                 speedInfo.trafficTime = qRound(m_reader->readElementText().toDouble());
633             } else if (m_reader->name() == QStringLiteral("BaseSpeed")) {
634                 speedInfo.baseSpeed = m_reader->readElementText().toDouble();
635             } else if (m_reader->name() == QStringLiteral("BaseTime")) {
636                 speedInfo.baseTime = qRound(m_reader->readElementText().toDouble());
637             } else {
638                 m_reader->skipCurrentElement();
639             }
640         }
641         m_reader->readNext();
642     }
643 
644     return !m_reader->hasError();
645 }
646 
647 QT_END_NAMESPACE
648