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