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