| 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 |  | 
| 52 | QGeoDynamicSpeedInfoContainer::QGeoDynamicSpeedInfoContainer() | 
| 53 | : trafficSpeed(0) | 
| 54 | , baseSpeed(0) | 
| 55 | , trafficTime(0) | 
| 56 | , baseTime(0) | 
| 57 | {} | 
| 58 |  | 
| 59 | QGeoRouteXmlParser::QGeoRouteXmlParser(const QGeoRouteRequest &request) | 
| 60 |         : m_request(request) | 
| 61 | { | 
| 62 | } | 
| 63 |  | 
| 64 | QGeoRouteXmlParser::~QGeoRouteXmlParser() | 
| 65 | { | 
| 66 | } | 
| 67 |  | 
| 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(runnable: this); | 
| 76 | } | 
| 77 |  | 
| 78 | void QGeoRouteXmlParser::run() | 
| 79 | { | 
| 80 |     m_reader = new QXmlStreamReader(m_data); | 
| 81 |  | 
| 82 |     if (!parseRootElement()) | 
| 83 |         emit error(errorString: m_reader->errorString()); | 
| 84 |     else | 
| 85 |         emit results(routes: m_results); | 
| 86 |  | 
| 87 |     delete m_reader; | 
| 88 |     m_reader = 0; | 
| 89 | } | 
| 90 |  | 
| 91 | bool QGeoRouteXmlParser::parseRootElement() | 
| 92 | { | 
| 93 |     if (!m_reader->readNextStartElement()) { | 
| 94 |         m_reader->raiseError(message: "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(qualifiedName: "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(message: QString("The root element is expected to have the name \"CalculateRoute\" or \"GetRoute\" (root element was named \"%1\")." ).arg(a: 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(message: QString("Expected a element named \"Response\" (element was named \"%1\")." ).arg(a: 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: &route)) | 
| 127 |                 continue; //route parsing failed move on to the next | 
| 128 |             m_results.append(t: 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 |  | 
| 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(strPoints: m_reader->readElementText(), geoPoints: &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: 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 |  | 
| 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(d: 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 |  | 
| 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(t: 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 |  | 
| 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(message: QString("Unsupported travel mode '\"%1\"'" ).arg(a: 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 |  | 
| 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 |  | 
| 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 |  | 
| 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(qualifiedName: "id" )) { | 
| 439 |         m_reader->raiseError(message: "The element \"Maneuver\" did not have the required attribute \"id\"." ); | 
| 440 |         return false; | 
| 441 |     } | 
| 442 |     QGeoManeuverContainer maneuverContainter; | 
| 443 |     maneuverContainter.id = m_reader->attributes().value(qualifiedName: "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(coord&: 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(strPoints: m_reader->readElementText(), geoPoints: &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(d: 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(t: maneuverContainter); | 
| 504 |     return true; | 
| 505 | } | 
| 506 |  | 
| 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(strPoints: m_reader->readElementText(), geoPoints: &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(t: segmentContainer); | 
| 545 |     return true; | 
| 546 | } | 
| 547 |  | 
| 548 | bool QGeoRouteXmlParser::parseGeoPoints(const QString &strPoints, QList<QGeoCoordinate> *geoPoints, const QString &elementName) | 
| 549 | { | 
| 550 |     QStringList rawPoints = strPoints.split(sep: ' '); | 
| 551 |  | 
| 552 |     for (int i = 0; i < rawPoints.length(); ++i) { | 
| 553 |         QStringList coords = rawPoints[i].split(sep: ','); | 
| 554 |  | 
| 555 |         if (coords.length() != 2) { | 
| 556 |             m_reader->raiseError(message: QString("Each of the space separated values of \"%1\" is expected to be a comma separated pair of coordinates (value was \"%2\")" ).arg(a: elementName).arg(a: rawPoints[i])); | 
| 557 |             return false; | 
| 558 |         } | 
| 559 |  | 
| 560 |         bool ok = false; | 
| 561 |         QString latString = coords[0]; | 
| 562 |         double lat = latString.toDouble(ok: &ok); | 
| 563 |  | 
| 564 |         if (!ok) { | 
| 565 |             m_reader->raiseError(message: QString("The latitude portions of \"%1\" are expected to have a value convertable to a double (value was \"%2\")" ).arg(a: elementName).arg(a: latString)); | 
| 566 |             return false; | 
| 567 |         } | 
| 568 |  | 
| 569 |         QString lngString = coords[1]; | 
| 570 |         double lng = lngString.toDouble(ok: &ok); | 
| 571 |  | 
| 572 |         if (!ok) { | 
| 573 |             m_reader->raiseError(message: QString("The longitude portions of \"%1\" are expected to have a value convertable to a double (value was \"%2\")" ).arg(a: elementName).arg(a: lngString)); | 
| 574 |             return false; | 
| 575 |         } | 
| 576 |  | 
| 577 |         QGeoCoordinate geoPoint(lat, lng); | 
| 578 |         geoPoints->append(t: geoPoint); | 
| 579 |     } | 
| 580 |  | 
| 581 |     return true; | 
| 582 | } | 
| 583 |  | 
| 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(coord&: coordinates)) | 
| 598 |                     tl = coordinates; | 
| 599 |             } else if (m_reader->name() == "BottomRight" ) { | 
| 600 |                 QGeoCoordinate coordinates; | 
| 601 |                 if (parseCoordinates(coord&: 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 |  | 
| 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(d: 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(d: 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 |  |