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
47 QT_BEGIN_NAMESPACE
48
49 // JSON reference: http://resources.arcgis.com/en/help/arcgis-rest-api/#/Route_service_with_synchronous_execution/02r300000036000000/
50
51 static const QString kErrorMessage(QStringLiteral("Error %1: %2."));
52 static const QString kErrorJson(QStringLiteral("Error: invalide JSON document."));
53
54 static const QString kErrorKey(QStringLiteral("error"));
55 static const QString kErrorCodeKey(QStringLiteral("code"));
56 static const QString kErrorMessageKey(QStringLiteral("message"));
57 static const QString kErrorDetailsKey(QStringLiteral("details"));
58 static const QString kDirectionsKey(QStringLiteral("directions"));
59 static const QString kRoutesKey(QStringLiteral("routes"));
60 static const QString kBarriersKey(QStringLiteral("barriers"));
61 static const QString kMessagesKey(QStringLiteral("messages"));
62 static const QString kDirectionsRouteIdKey(QStringLiteral("routeId"));
63 static const QString kDirectionsRouteNameKey(QStringLiteral("routeName"));
64 static const QString kDirectionsSummaryKey(QStringLiteral("summary"));
65 static const QString kDirectionsTotalLengthKey(QStringLiteral("totalLength"));
66 static const QString kDirectionsTotalTimeKey(QStringLiteral("totalTime"));
67 static const QString kDirectionsTotalDriveTimeKey(QStringLiteral("totalDriveTime"));
68 static const QString kDirectionsEnvelopeKey(QStringLiteral("envelope"));
69 static const QString kDirectionsEnvelopeXminKey(QStringLiteral("xmin"));
70 static const QString kDirectionsEnvelopeYminKey(QStringLiteral("ymin"));
71 static const QString kDirectionsEnvelopeXmaxKey(QStringLiteral("xmax"));
72 static const QString kDirectionsEnvelopeYmaxKey(QStringLiteral("ymax"));
73 static const QString kDirectionsFeaturesKey(QStringLiteral("features"));
74 static const QString kDirectionsFeaturesAttributesKey(QStringLiteral("attributes"));
75 static const QString kDirectionsFeaturesCompressedGeometryKey(QStringLiteral("compressedGeometry"));
76 static const QString kDirectionsFeaturesAttributesLengthKey(QStringLiteral("length"));
77 static const QString kDirectionsFeaturesAttributesTimeKey(QStringLiteral("time"));
78 static const QString kDirectionsFeaturesAttributesTextKey(QStringLiteral("text"));
79 static const QString kDirectionsFeaturesAttributesEtaKey(QStringLiteral("ETA"));
80 static const QString kDirectionsFeaturesAttributesManeuverTypeKey(QStringLiteral("maneuverType"));
81 static const QString kRoutesFeaturesKey(QStringLiteral("features"));
82 static const QString kRoutesFeaturesAttributesKey(QStringLiteral("attributes"));
83 static const QString kRoutesFeaturesObjectIdKey(QStringLiteral("ObjectID"));
84 static const QString kRoutesFeaturesGeometryKey(QStringLiteral("geometry"));
85 static const QString kRoutesFeaturesGeometryPathsKey(QStringLiteral("paths"));
86
GeoRouteJsonParserEsri(const QJsonDocument & document)87 GeoRouteJsonParserEsri::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(kErrorKey))
97 {
98 QJsonObject error = m_json.value(kErrorKey).toObject();
99 int code = error.value(kErrorCodeKey).toInt();
100 QString message = error.value(kErrorMessageKey).toString();
101
102 m_error = kErrorMessage.arg(code).arg(message);
103 return;
104 }
105
106 parseDirections();
107 parseRoutes();
108 }
109
routes() const110 QList<QGeoRoute> GeoRouteJsonParserEsri::routes() const
111 {
112 return m_routes.values();
113 }
114
isValid() const115 bool GeoRouteJsonParserEsri::isValid() const
116 {
117 return (m_error.isEmpty());
118 }
119
errorString() const120 QString GeoRouteJsonParserEsri::errorString() const
121 {
122 return m_error;
123 }
124
parseDirections()125 void GeoRouteJsonParserEsri::parseDirections()
126 {
127 QJsonArray directions = m_json.value(kDirectionsKey).toArray();
128 foreach (const QJsonValue &direction, directions)
129 parseDirection(direction.toObject());
130 }
131
parseDirection(const QJsonObject & direction)132 void GeoRouteJsonParserEsri::parseDirection(const QJsonObject &direction)
133 {
134 QGeoRoute &geoRoute = m_routes[direction.value(kDirectionsRouteIdKey).toInt()];
135
136 // parse summary
137 geoRoute.setRouteId(direction.value(kDirectionsRouteNameKey).toString());
138
139 QJsonObject summary = direction.value(kDirectionsSummaryKey).toObject();
140 geoRoute.setDistance(summary.value(kDirectionsTotalLengthKey).toDouble());
141
142 geoRoute.setTravelTime(summary.value(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(kDirectionsEnvelopeKey).toObject();
149
150 QGeoCoordinate topLeft(enveloppe.value(kDirectionsEnvelopeXminKey).toDouble(),
151 enveloppe.value(kDirectionsEnvelopeYmaxKey).toDouble());
152 QGeoCoordinate bottomRight(enveloppe.value(kDirectionsEnvelopeXmaxKey).toDouble(),
153 enveloppe.value(kDirectionsEnvelopeYminKey).toDouble());
154 geoRoute.setBounds(QGeoRectangle(topLeft, bottomRight));
155
156 // parse features
157 QJsonArray features = direction.value(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(kDirectionsFeaturesAttributesKey).toObject();
189
190 QGeoRouteSegment segment;
191 double length = attributes.value(kDirectionsFeaturesAttributesLengthKey).toDouble();
192 segment.setDistance(length);
193
194 double time = attributes.value(kDirectionsFeaturesAttributesTimeKey).toDouble() * 60;
195 // default units is minutes, see directionsTimeAttributeName param
196 segment.setTravelTime(time);
197
198 QGeoManeuver maneuver;
199 QString type = attributes.value(kDirectionsFeaturesAttributesManeuverTypeKey).toString();
200 maneuver.setDirection(esriDirectionsManeuverTypes.value(type));
201
202 maneuver.setInstructionText(attributes.value(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
parseRoutes()214 void GeoRouteJsonParserEsri::parseRoutes()
215 {
216 QJsonObject routes = m_json.value(kRoutesKey).toObject();
217 QJsonArray features = routes.value(kRoutesFeaturesKey).toArray();
218 foreach (const QJsonValue &feature, features)
219 parseRoute(feature.toObject());
220 }
221
parseRoute(const QJsonObject & route)222 void GeoRouteJsonParserEsri::parseRoute(const QJsonObject &route)
223 {
224 QJsonObject attributes = route.value(kRoutesFeaturesAttributesKey).toObject();
225 QGeoRoute &geoRoute = m_routes[attributes.value(kRoutesFeaturesObjectIdKey).toInt()];
226
227 QJsonObject geometry = route.value(kRoutesFeaturesGeometryKey).toObject();
228 QJsonArray paths = geometry.value(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(QGeoCoordinate(geoCoordinate[1].toDouble(),
239 geoCoordinate[0].toDouble()));
240 }
241 }
242 geoRoute.setPath(geoCoordinates);
243 }
244 }
245
246 QT_END_NAMESPACE
247