1/****************************************************************************
2**
3** Copyright (C) 2013-2018 Esri <contracts@esri.com>
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtLocation module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
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 https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "georoutejsonparser_esri.h"
41
42#include <QJsonArray>
43#include <QGeoRectangle>
44#include <QGeoManeuver>
45#include <QGeoRouteSegment>
46
47QT_BEGIN_NAMESPACE
48
49// JSON reference: http://resources.arcgis.com/en/help/arcgis-rest-api/#/Route_service_with_synchronous_execution/02r300000036000000/
50
51static const QString kErrorMessage(QStringLiteral("Error %1: %2."));
52static const QString kErrorJson(QStringLiteral("Error: invalide JSON document."));
53
54static const QString kErrorKey(QStringLiteral("error"));
55static const QString kErrorCodeKey(QStringLiteral("code"));
56static const QString kErrorMessageKey(QStringLiteral("message"));
57static const QString kErrorDetailsKey(QStringLiteral("details"));
58static const QString kDirectionsKey(QStringLiteral("directions"));
59static const QString kRoutesKey(QStringLiteral("routes"));
60static const QString kBarriersKey(QStringLiteral("barriers"));
61static const QString kMessagesKey(QStringLiteral("messages"));
62static const QString kDirectionsRouteIdKey(QStringLiteral("routeId"));
63static const QString kDirectionsRouteNameKey(QStringLiteral("routeName"));
64static const QString kDirectionsSummaryKey(QStringLiteral("summary"));
65static const QString kDirectionsTotalLengthKey(QStringLiteral("totalLength"));
66static const QString kDirectionsTotalTimeKey(QStringLiteral("totalTime"));
67static const QString kDirectionsTotalDriveTimeKey(QStringLiteral("totalDriveTime"));
68static const QString kDirectionsEnvelopeKey(QStringLiteral("envelope"));
69static const QString kDirectionsEnvelopeXminKey(QStringLiteral("xmin"));
70static const QString kDirectionsEnvelopeYminKey(QStringLiteral("ymin"));
71static const QString kDirectionsEnvelopeXmaxKey(QStringLiteral("xmax"));
72static const QString kDirectionsEnvelopeYmaxKey(QStringLiteral("ymax"));
73static const QString kDirectionsFeaturesKey(QStringLiteral("features"));
74static const QString kDirectionsFeaturesAttributesKey(QStringLiteral("attributes"));
75static const QString kDirectionsFeaturesCompressedGeometryKey(QStringLiteral("compressedGeometry"));
76static const QString kDirectionsFeaturesAttributesLengthKey(QStringLiteral("length"));
77static const QString kDirectionsFeaturesAttributesTimeKey(QStringLiteral("time"));
78static const QString kDirectionsFeaturesAttributesTextKey(QStringLiteral("text"));
79static const QString kDirectionsFeaturesAttributesEtaKey(QStringLiteral("ETA"));
80static const QString kDirectionsFeaturesAttributesManeuverTypeKey(QStringLiteral("maneuverType"));
81static const QString kRoutesFeaturesKey(QStringLiteral("features"));
82static const QString kRoutesFeaturesAttributesKey(QStringLiteral("attributes"));
83static const QString kRoutesFeaturesObjectIdKey(QStringLiteral("ObjectID"));
84static const QString kRoutesFeaturesGeometryKey(QStringLiteral("geometry"));
85static const QString kRoutesFeaturesGeometryPathsKey(QStringLiteral("paths"));
86
87GeoRouteJsonParserEsri::GeoRouteJsonParserEsri(const QJsonDocument &document)
88{
89 if (!document.isObject())
90 {
91 m_error = kErrorJson;
92 return;
93 }
94
95 m_json = document.object();
96 if (m_json.contains(key: kErrorKey))
97 {
98 QJsonObject error = m_json.value(key: kErrorKey).toObject();
99 int code = error.value(key: kErrorCodeKey).toInt();
100 QString message = error.value(key: kErrorMessageKey).toString();
101
102 m_error = kErrorMessage.arg(a: code).arg(a: message);
103 return;
104 }
105
106 parseDirections();
107 parseRoutes();
108}
109
110QList<QGeoRoute> GeoRouteJsonParserEsri::routes() const
111{
112 return m_routes.values();
113}
114
115bool GeoRouteJsonParserEsri::isValid() const
116{
117 return (m_error.isEmpty());
118}
119
120QString GeoRouteJsonParserEsri::errorString() const
121{
122 return m_error;
123}
124
125void GeoRouteJsonParserEsri::parseDirections()
126{
127 QJsonArray directions = m_json.value(key: kDirectionsKey).toArray();
128 foreach (const QJsonValue &direction, directions)
129 parseDirection(direction: direction.toObject());
130}
131
132void GeoRouteJsonParserEsri::parseDirection(const QJsonObject &direction)
133{
134 QGeoRoute &geoRoute = m_routes[direction.value(key: kDirectionsRouteIdKey).toInt()];
135
136 // parse summary
137 geoRoute.setRouteId(direction.value(key: kDirectionsRouteNameKey).toString());
138
139 QJsonObject summary = direction.value(key: kDirectionsSummaryKey).toObject();
140 geoRoute.setDistance(summary.value(key: kDirectionsTotalLengthKey).toDouble());
141
142 geoRoute.setTravelTime(summary.value(key: kDirectionsTotalTimeKey).toDouble() * 60);
143 // default units is minutes, see directionsTimeAttributeName param
144
145 geoRoute.setTravelMode(QGeoRouteRequest::CarTravel);
146 // default request is time for car, see directionsTimeAttributeName param
147
148 QJsonObject enveloppe = summary.value(key: kDirectionsEnvelopeKey).toObject();
149
150 QGeoCoordinate topLeft(enveloppe.value(key: kDirectionsEnvelopeXminKey).toDouble(),
151 enveloppe.value(key: kDirectionsEnvelopeYmaxKey).toDouble());
152 QGeoCoordinate bottomRight(enveloppe.value(key: kDirectionsEnvelopeXmaxKey).toDouble(),
153 enveloppe.value(key: kDirectionsEnvelopeYminKey).toDouble());
154 geoRoute.setBounds(QGeoRectangle(topLeft, bottomRight));
155
156 // parse features
157 QJsonArray features = direction.value(key: kDirectionsFeaturesKey).toArray();
158
159 static const QMap<QString, QGeoManeuver::InstructionDirection> esriDirectionsManeuverTypes
160 {
161 { QStringLiteral("esriDMTUnknown"), QGeoManeuver::NoDirection },
162 { QStringLiteral("esriDMTStop"), QGeoManeuver::NoDirection },
163 { QStringLiteral("esriDMTStraight"), QGeoManeuver::DirectionForward },
164 { QStringLiteral("esriDMTBearLeft"), QGeoManeuver::DirectionBearLeft },
165 { QStringLiteral("esriDMTBearRight"), QGeoManeuver::DirectionBearRight },
166 { QStringLiteral("esriDMTTurnLeft"), QGeoManeuver::DirectionLeft },
167 { QStringLiteral("esriDMTTurnRight"), QGeoManeuver::DirectionRight },
168 { QStringLiteral("esriDMTSharpLeft"), QGeoManeuver::DirectionLightLeft },
169 { QStringLiteral("esriDMTSharpRight"), QGeoManeuver::DirectionLightRight },
170 { QStringLiteral("esriDMTUTurn"), QGeoManeuver::DirectionUTurnRight },
171 { QStringLiteral("esriDMTFerry"), QGeoManeuver::NoDirection },
172 { QStringLiteral("esriDMTRoundabout"), QGeoManeuver::NoDirection },
173 { QStringLiteral("esriDMTHighwayMerge"), QGeoManeuver::NoDirection },
174 { QStringLiteral("esriDMTHighwayExit"), QGeoManeuver::NoDirection },
175 { QStringLiteral("esriDMTHighwayChange"), QGeoManeuver::NoDirection },
176 { QStringLiteral("esriDMTForkCenter"), QGeoManeuver::NoDirection },
177 { QStringLiteral("esriDMTForkLeft"), QGeoManeuver::NoDirection },
178 { QStringLiteral("esriDMTForkRight"), QGeoManeuver::NoDirection },
179 { QStringLiteral("esriDMTDepart"), QGeoManeuver::NoDirection },
180 { QStringLiteral("esriDMTTripItem"), QGeoManeuver::NoDirection },
181 { QStringLiteral("esriDMTEndOfFerry"), QGeoManeuver::NoDirection }
182 };
183
184 QGeoRouteSegment firstSegment;
185 for (int i = features.size() - 1; i >= 0; --i)
186 {
187 QJsonObject feature = features.at(i).toObject();
188 QJsonObject attributes = feature.value(key: kDirectionsFeaturesAttributesKey).toObject();
189
190 QGeoRouteSegment segment;
191 double length = attributes.value(key: kDirectionsFeaturesAttributesLengthKey).toDouble();
192 segment.setDistance(length);
193
194 double time = attributes.value(key: kDirectionsFeaturesAttributesTimeKey).toDouble() * 60;
195 // default units is minutes, see directionsTimeAttributeName param
196 segment.setTravelTime(time);
197
198 QGeoManeuver maneuver;
199 QString type = attributes.value(key: kDirectionsFeaturesAttributesManeuverTypeKey).toString();
200 maneuver.setDirection(esriDirectionsManeuverTypes.value(akey: type));
201
202 maneuver.setInstructionText(attributes.value(key: kDirectionsFeaturesAttributesTextKey).toString() + ".");
203 maneuver.setDistanceToNextInstruction(length);
204 maneuver.setTimeToNextInstruction(time);
205
206 segment.setManeuver(maneuver);
207
208 segment.setNextRouteSegment(firstSegment);
209 firstSegment = segment;
210 }
211 geoRoute.setFirstRouteSegment(firstSegment);
212}
213
214void GeoRouteJsonParserEsri::parseRoutes()
215{
216 QJsonObject routes = m_json.value(key: kRoutesKey).toObject();
217 QJsonArray features = routes.value(key: kRoutesFeaturesKey).toArray();
218 foreach (const QJsonValue &feature, features)
219 parseRoute(route: feature.toObject());
220}
221
222void GeoRouteJsonParserEsri::parseRoute(const QJsonObject &route)
223{
224 QJsonObject attributes = route.value(key: kRoutesFeaturesAttributesKey).toObject();
225 QGeoRoute &geoRoute = m_routes[attributes.value(key: kRoutesFeaturesObjectIdKey).toInt()];
226
227 QJsonObject geometry = route.value(key: kRoutesFeaturesGeometryKey).toObject();
228 QJsonArray paths = geometry.value(key: kRoutesFeaturesGeometryPathsKey).toArray();
229
230 if (!paths.isEmpty())
231 {
232 QList<QGeoCoordinate> geoCoordinates;
233 foreach (const QJsonValue &value, paths.first().toArray()) // only first polyline?
234 {
235 QJsonArray geoCoordinate = value.toArray();
236 if (geoCoordinate.size() == 2) // ignore 3rd coordinate
237 {
238 geoCoordinates.append(t: QGeoCoordinate(geoCoordinate[1].toDouble(),
239 geoCoordinate[0].toDouble()));
240 }
241 }
242 geoRoute.setPath(geoCoordinates);
243 }
244}
245
246QT_END_NAMESPACE
247

source code of qtlocation/src/plugins/geoservices/esri/georoutejsonparser_esri.cpp