Measurement units for routing are selected based on device locale setting
[situare] / src / routing / routingservice.cpp
1 /*
2    Situare - A location system for Facebook
3    Copyright (C) 2010  Ixonos Plc. Authors:
4
5       Henri Lampela - henri.lampela@ixonos.com
6       Sami Rämö - sami.ramo@ixonos.com
7
8    Situare is free software; you can redistribute it and/or
9    modify it under the terms of the GNU General Public License
10    version 2 as published by the Free Software Foundation.
11
12    Situare is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with Situare; if not, write to the Free Software
19    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
20    USA.
21 */
22
23 #include "parser.h"
24
25 #include <QCryptographicHash>
26 #include <QDebug>
27 #include <QLocale>
28 #include <QNetworkReply>
29 #include <QStringList>
30 #include <QtGlobal>
31
32 #if defined(Q_WS_MAEMO_5) & defined(ARMEL)
33 #include <QDBusInterface>
34 #include <QDBusMessage>
35 #endif
36
37 #include "coordinates/geocoordinate.h"
38 #include "../error.h"
39 #include "location.h"
40 #include "network/networkaccessmanager.h"
41 #include "route.h"
42 #include "routesegment.h"
43
44 #include "routingservice.h"
45
46 // defines for IMEI retrieval via dbus
47 #define SIM_DBUS_SERVICE  "com.nokia.phone.SIM"
48 #define SIM_DBUS_PATH     "/com/nokia/phone/SIM/security"
49 #define SIM_DBUS_IFACE    "Phone.Sim.Security"
50 #define SIM_IMEI_REQ      "get_imei"
51
52 const int NO_ERROR = 0;
53
54 // cloudmade
55 const QString CLOUDMADE_AUTH_PART = "http://auth.cloudmade.com/token/";
56 const QString CLOUDMADE_API_KEY = "0cea0072adbf42ce823f4c78b974f12d";
57
58 RoutingService::RoutingService(QObject *parent)
59         : QObject(parent)
60 {
61     qDebug() << __PRETTY_FUNCTION__;
62
63     m_pendingRequest.clear();
64     m_token.clear();
65
66     m_networkManager = new NetworkAccessManager(this);
67     connect(m_networkManager, SIGNAL(finished(QNetworkReply*)),
68             this, SLOT(requestFinished(QNetworkReply*)), Qt::QueuedConnection);
69
70 }
71
72 void RoutingService::parseRouteData(const QByteArray &jsonReply)
73 {
74     qDebug() << __PRETTY_FUNCTION__;
75
76     QJson::Parser parser;
77     bool ok;
78     QVariantMap result = parser.parse (jsonReply, &ok).toMap();
79     if (!ok) {
80         emit error(ErrorContext::SITUARE, SituareError::ERROR_ROUTING_FAILED);
81         return;
82     } else if(result.value("status").toInt() == NO_ERROR) {
83         QVariant routeSummary = result.value("route_summary");
84         QMap<QString, QVariant> routeSummaryMap = routeSummary.toMap();
85
86         Route route;
87         route.setEndPointName(routeSummaryMap["end_point"].toString());
88         route.setStartPointName(routeSummaryMap["start_point"].toString());
89         route.setTotalDistance(routeSummaryMap["total_distance"].toInt());
90         route.setTotalTime(routeSummaryMap["total_time"].toInt());
91
92         foreach(QVariant routeGeometry, result["route_geometry"].toList()) {
93             QStringList list = routeGeometry.toStringList();
94             route.appendGeometryPoint(GeoCoordinate(list.at(0).toDouble(), list.at(1).toDouble()));
95         }
96
97         foreach(QVariant routeInstructions, result["route_instructions"].toList()) {
98             QStringList list = routeInstructions.toStringList();
99             RouteSegment segment;
100             segment.setInstruction(list.at(0));
101             segment.setLength(list.at(1).toDouble());
102             segment.setPositionIndex(list.at(2).toInt());
103             segment.setTime(list.at(3).toInt());
104             segment.setLengthCaption(list.at(4));
105             segment.setEarthDirection(list.at(5));
106             segment.setAzimuth(list.at(6).toDouble());
107             if (list.count() == 9) {
108                 segment.setTurnType(list.at(7));
109                 segment.setTurnAngle(list.at(8).toDouble());
110             }
111             route.appendSegment(segment);
112         }
113
114         emit routeParsed(route);
115     } else {
116         emit error(ErrorContext::SITUARE, SituareError::ERROR_ROUTING_FAILED);
117         qCritical() << __PRETTY_FUNCTION__ << "routing failed, json status_message:"
118                                            << result.value("status_message").toString();
119     }
120 }
121
122 void RoutingService::requestAuthorizationToken()
123 {
124     qDebug() << __PRETTY_FUNCTION__;
125
126     // create token request of format: http://auth.cloudmade.com/token/APIKEY?userid=UserID
127     // where APIKEY is our application specific key and userID is md5 hashed value of IMEI code
128     QString url = CLOUDMADE_AUTH_PART;
129     url.append(CLOUDMADE_API_KEY);
130     QString uidPart = "?userid=";
131     QByteArray rawData;
132
133 #if defined(Q_WS_MAEMO_5) & defined(ARMEL)
134     // get the device IMEI code
135     QDBusInterface interface(SIM_DBUS_SERVICE, SIM_DBUS_PATH, SIM_DBUS_IFACE,
136                              QDBusConnection::systemBus());
137
138     QDBusMessage reply = interface.call(SIM_IMEI_REQ);
139     if (reply.type() == QDBusMessage::ErrorMessage)
140         qDebug() << reply.errorMessage();
141     else {
142         QList<QVariant> args = reply.arguments();
143         qDebug() << QString("Phone's IMEI: %1").arg(args.at(0).toString());
144         rawData = args.at(0).toByteArray();
145     }
146 #else
147     // for desktop we need to use login name
148     rawData.append(system("whoami"));
149 #endif
150
151     QCryptographicHash crypto(QCryptographicHash::Md5);
152     crypto.addData(rawData);
153     uidPart.append(crypto.result().toHex());
154
155     url.append(uidPart);
156     sendRequest(url);
157 }
158
159 void RoutingService::requestFinished(QNetworkReply *reply)
160 {
161     qDebug() << __PRETTY_FUNCTION__;
162
163     QByteArray replyArray = reply->readAll();
164
165     if (reply->error()) {
166         emit error(ErrorContext::NETWORK, reply->error());
167     } else if(reply->url().toString().contains(CLOUDMADE_AUTH_PART)) {
168         m_token = replyArray;
169         m_pendingRequest.append(m_token);
170         sendRequest(m_pendingRequest);
171         m_pendingRequest.clear();
172     } else {
173         parseRouteData(replyArray);
174     }
175
176     reply->deleteLater();
177 }
178
179 void RoutingService::requestRoute(const GeoCoordinate &from, const GeoCoordinate &to)
180 {
181     qDebug() << __PRETTY_FUNCTION__;
182
183     QString url = "http://routes.cloudmade.com/";
184     url.append(CLOUDMADE_API_KEY);
185     url.append("/api/0.3/");
186     url.append(QString::number(from.latitude()) + "," + QString::number(from.longitude()) + ",");
187     url.append(QString::number(to.latitude()) + "," + QString::number(to.longitude()));
188     url.append("/car/fastest.js?lang=en");
189
190     if (QLocale::system().measurementSystem() == QLocale::ImperialSystem)
191         url.append("&units=miles");
192     else
193         url.append("&units=km");
194
195     url.append("&token=");
196     if(m_token.isEmpty()) {
197         m_pendingRequest.append(url);
198         requestAuthorizationToken();
199     } else {
200         url.append(m_token);
201         sendRequest(QUrl(url));
202     }
203 }
204
205 void RoutingService::sendRequest(const QUrl &url)
206 {
207     qDebug() << __PRETTY_FUNCTION__;
208
209     QNetworkRequest request;
210
211     request.setUrl(url);
212     request.setAttribute(QNetworkRequest::CacheSaveControlAttribute, false);
213
214     QByteArray ba;
215     m_networkManager->post(request, ba, false);
216 }