27 #include <QNetworkAccessManager>
28 #include <QNetworkReply>
29 #include <QScriptValue>
30 #include <QScriptEngine>
31 #include <QScriptValueIterator>
36 QVector<QPair<GeoDataCoordinates,QString> > OSRMRunner:: m_cachedHints;
38 QString OSRMRunner:: m_hintChecksum;
42 m_networkAccessManager()
44 connect( &m_networkAccessManager, SIGNAL(finished(QNetworkReply*)),
45 this, SLOT(retrieveData(QNetworkReply*)) );
55 if ( route->
size() < 2 ) {
59 QString url =
"http://router.project-osrm.org/viaroute?output=json&instructions=true";
61 bool appendChecksum =
false;
62 typedef QPair<GeoDataCoordinates,QString> CachePair;
63 QVector<CachePair> newChecksums;
64 QString
const invalidEntry =
"invalid";
65 for (
int i=0; i<route->
size(); ++i ) {
67 append( &url,
"loc", QString::number( coordinates.
latitude( degree ),
'f', 6 ) +
',' + QString::number( coordinates.
longitude( degree ),
'f', 6 ) );
68 foreach(
const CachePair &hint, m_cachedHints ) {
69 if ( hint.first == coordinates && hint.second != invalidEntry && m_hintChecksum != invalidEntry ) {
70 append( &url,
"hint", hint.second );
71 appendChecksum =
true;
74 newChecksums << CachePair( coordinates, invalidEntry );
77 if ( appendChecksum ) {
78 append( &url,
"checksum", m_hintChecksum );
81 m_cachedHints = newChecksums;
82 m_hintChecksum = invalidEntry;
84 m_request = QNetworkRequest( QUrl( url ) );
90 timer.setSingleShot(
true );
91 timer.setInterval( 15000 );
93 connect( &timer, SIGNAL(timeout()),
94 &eventLoop, SLOT(quit()));
96 &eventLoop, SLOT(quit()) );
99 QTimer::singleShot( 0,
this, SLOT(
get()) );
105 void OSRMRunner::retrieveData( QNetworkReply *reply )
107 if ( reply->isFinished() ) {
108 QByteArray data = reply->readAll();
109 reply->deleteLater();
113 mDebug() <<
"Failed to parse the downloaded route data" << data;
120 void OSRMRunner::handleError( QNetworkReply::NetworkError error )
122 mDebug() <<
" Error when retrieving OSRM route: " << error;
125 void OSRMRunner::get()
127 QNetworkReply *reply = m_networkAccessManager.get( m_request );
128 connect( reply, SIGNAL(error(QNetworkReply::NetworkError)),
129 this, SLOT(handleError(QNetworkReply::NetworkError)), Qt::DirectConnection );
132 void OSRMRunner::append(QString *input,
const QString &key,
const QString &value)
const
134 *input +=
'&' + key +
'=' + value;
137 GeoDataLineString *OSRMRunner::decodePolyline(
const QString &geometry )
const
140 GeoDataLineString* lineString =
new GeoDataLineString;
141 int coordinates[2] = { 0, 0 };
142 int const length = geometry.length();
143 for(
int i=0; i<length; ) {
144 for (
int j=0; j<2; ++j ) {
145 int block( 0 ), shift( 0 ), result( 0 );
147 block = geometry.at( i++ ).toLatin1() - 63;
148 result |= ( block & 0x1F ) << shift;
150 }
while ( block >= 0x20 );
151 coordinates[j] += ( ( result & 1 ) != 0 ? ~( result >> 1 ) : ( result >> 1 ) );
153 lineString->append( GeoDataCoordinates(
double( coordinates[1] ) / 1E6,
154 double( coordinates[0] ) / 1E6,
162 if ( instruction ==
"1" ) {
164 }
else if ( instruction ==
"2" ) {
166 }
else if ( instruction ==
"3" ) {
168 }
else if ( instruction ==
"4" ) {
170 }
else if ( instruction ==
"5" ) {
172 }
else if ( instruction ==
"6" ) {
174 }
else if ( instruction ==
"7" ) {
176 }
else if ( instruction ==
"8" ) {
178 }
else if ( instruction ==
"10" ) {
180 }
else if ( instruction.startsWith( QLatin1String(
"11-" ) ) ) {
181 int const exit = instruction.mid( 3 ).toInt();
188 }
else if ( instruction ==
"12" ) {
200 GeoDataDocument *OSRMRunner::parse(
const QByteArray &input )
202 QScriptEngine engine;
204 QScriptValue
const data = engine.evaluate(
'(' + QString::fromUtf8( input ) +
')' );
206 GeoDataDocument* result = 0;
207 GeoDataLineString* routeWaypoints = 0;
208 if ( data.property(
"route_geometry" ).isString() ) {
209 result =
new GeoDataDocument();
210 result->setName(
"Open Source Routing Machine" );
211 GeoDataPlacemark* routePlacemark =
new GeoDataPlacemark;
212 routePlacemark->setName(
"Route" );
213 routeWaypoints = decodePolyline( data.property(
"route_geometry" ).toString() );
214 routePlacemark->setGeometry( routeWaypoints );
216 QString name =
"%1 %2 (OSRM)";
217 QString unit = QLatin1String(
"m" );
219 if (length >= 1000) {
223 result->setName( name.arg( length, 0,
'f', 1 ).arg( unit ) );
224 result->append( routePlacemark );
227 if ( result && routeWaypoints && data.property(
"route_instructions" ).isArray() ) {
229 QScriptValueIterator iterator( data.property(
"route_instructions" ) );
230 GeoDataPlacemark* instruction =
new GeoDataPlacemark;
231 int lastWaypointIndex = 0;
232 while ( iterator.hasNext() ) {
234 QVariantList details = iterator.value().toVariant().toList();
235 if ( details.size() > 7 ) {
236 QString
const text = details.at( 0 ).toString();
237 QString
const road = details.at( 1 ).toString();
238 int const waypointIndex = details.at( 3 ).toInt();
240 if ( waypointIndex < routeWaypoints->size() ) {
241 if ( iterator.hasNext() ) {
242 GeoDataLineString *lineString =
new GeoDataLineString;
243 for (
int i=lastWaypointIndex; i<=waypointIndex; ++i ) {
244 lineString->append(routeWaypoints->at( i ) );
246 instruction->setGeometry( lineString );
247 result->append( instruction );
248 instruction =
new GeoDataPlacemark;
250 lastWaypointIndex = waypointIndex;
251 GeoDataExtendedData extendedData;
252 GeoDataData turnTypeData;
253 turnTypeData.setName(
"turnType" );
255 turnTypeData.setValue( turnType );
256 extendedData.addValue( turnTypeData );
257 if (!road.isEmpty()) {
258 GeoDataData roadName;
259 roadName.setName(
"roadName" );
260 roadName.setValue( road );
261 extendedData.addValue( roadName );
270 instruction->setName( text );
274 instruction->setExtendedData( extendedData );
276 if ( !iterator.hasNext() && lastWaypointIndex > 0 ) {
277 GeoDataLineString *lineString =
new GeoDataLineString;
278 for (
int i=lastWaypointIndex; i<waypointIndex; ++i ) {
279 lineString->append(routeWaypoints->at( i ) );
281 instruction->setGeometry( lineString );
282 result->append( instruction );
289 if ( data.property(
"hint_data" ).isValid() ) {
290 QVariantList hints = data.property(
"hint_data" ).property(
"locations" ).toVariant().toList();
291 if ( hints.size() == m_cachedHints.size() ) {
292 for (
int i=0; i<m_cachedHints.size(); ++i ) {
293 m_cachedHints[i].second = hints[i].toString();
297 m_hintChecksum = data.property(
"hint_data" ).property(
"checksum" ).toString();
305 #include "OSRMRunner.moc"
Unit
enum used constructor to specify the units used
int size() const
Number of points in the route.
A 3d point representation.
A container for Features, Styles and in the future Schemas.
qreal latitude(GeoDataCoordinates::Unit unit=GeoDataCoordinates::Radian) const
retrieves the latitude of the GeoDataCoordinates object use the unit parameter to switch between Radi...
static QString generateRoadInstruction(TurnType turnType, const QString &roadName)
Points to be included in a route.
virtual void retrieveRoute(const RouteRequest *request)
Start a route download orw calculation.
OSRMRunner(QObject *parent=0)
qreal longitude(GeoDataCoordinates::Unit unit=GeoDataCoordinates::Radian) const
retrieves the longitude of the GeoDataCoordinates object use the unit parameter to switch between Rad...
void routeCalculated(GeoDataDocument *route)
Route download/calculation is finished, result in the given route object.
static QByteArray userAgent(const QString &platform, const QString &plugin)
QDebug mDebug()
a function to replace qDebug() in Marble library code
GeoDataCoordinates at(int index) const
Accessor for the n-th position.