• Skip to content
  • Skip to link menu
KDE API Reference
  • KDE API Reference
  • kdeedu API Reference
  • KDE Home
  • Contact Us
 

marble

  • sources
  • kde-4.14
  • kdeedu
  • marble
  • src
  • plugins
  • runner
  • open-source-routing-machine
OSRMRunner.cpp
Go to the documentation of this file.
1 //
2 // This file is part of the Marble Virtual Globe.
3 //
4 // This program is free software licensed under the GNU LGPL. You can
5 // find a copy of this license in LICENSE.txt in the top directory of
6 // the source code.
7 //
8 // Copyright 2012 Dennis Nienhüser <earthwings@gentoo.org>
9 //
10 
11 #include "OSRMRunner.h"
12 
13 #include "MarbleDebug.h"
14 #include "MarbleLocale.h"
15 #include "GeoDataDocument.h"
16 #include "GeoDataPlacemark.h"
17 #include "GeoDataExtendedData.h"
18 #include "routing/Maneuver.h"
19 #include "routing/RouteRequest.h"
20 #include "TinyWebBrowser.h"
21 
22 #include <QString>
23 #include <QVector>
24 #include <QUrl>
25 #include <QTime>
26 #include <QTimer>
27 #include <QNetworkAccessManager>
28 #include <QNetworkReply>
29 #include <QScriptValue>
30 #include <QScriptEngine>
31 #include <QScriptValueIterator>
32 
33 namespace Marble
34 {
35 
36 QVector<QPair<GeoDataCoordinates,QString> > OSRMRunner:: m_cachedHints;
37 
38 QString OSRMRunner:: m_hintChecksum;
39 
40 OSRMRunner::OSRMRunner( QObject *parent ) :
41  RoutingRunner( parent ),
42  m_networkAccessManager()
43 {
44  connect( &m_networkAccessManager, SIGNAL(finished(QNetworkReply*)),
45  this, SLOT(retrieveData(QNetworkReply*)) );
46 }
47 
48 OSRMRunner::~OSRMRunner()
49 {
50  // nothing to do
51 }
52 
53 void OSRMRunner::retrieveRoute( const RouteRequest *route )
54 {
55  if ( route->size() < 2 ) {
56  return;
57  }
58 
59  QString url = "http://router.project-osrm.org/viaroute?output=json&instructions=true";
60  GeoDataCoordinates::Unit const degree = GeoDataCoordinates::Degree;
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 ) {
66  GeoDataCoordinates const coordinates = route->at( 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;
72  }
73  }
74  newChecksums << CachePair( coordinates, invalidEntry );
75  }
76 
77  if ( appendChecksum ) {
78  append( &url, "checksum", m_hintChecksum );
79  }
80 
81  m_cachedHints = newChecksums;
82  m_hintChecksum = invalidEntry;
83 
84  m_request = QNetworkRequest( QUrl( url ) );
85  m_request.setRawHeader( "User-Agent", TinyWebBrowser::userAgent( "Browser", "OSRMRunner" ) );
86 
87  QEventLoop eventLoop;
88 
89  QTimer timer;
90  timer.setSingleShot( true );
91  timer.setInterval( 15000 );
92 
93  connect( &timer, SIGNAL(timeout()),
94  &eventLoop, SLOT(quit()));
95  connect( this, SIGNAL(routeCalculated(GeoDataDocument*)),
96  &eventLoop, SLOT(quit()) );
97 
98  // @todo FIXME Must currently be done in the main thread, see bug 257376
99  QTimer::singleShot( 0, this, SLOT(get()) );
100  timer.start();
101 
102  eventLoop.exec();
103 }
104 
105 void OSRMRunner::retrieveData( QNetworkReply *reply )
106 {
107  if ( reply->isFinished() ) {
108  QByteArray data = reply->readAll();
109  reply->deleteLater();
110  GeoDataDocument* document = parse( data );
111 
112  if ( !document ) {
113  mDebug() << "Failed to parse the downloaded route data" << data;
114  }
115 
116  emit routeCalculated( document );
117  }
118 }
119 
120 void OSRMRunner::handleError( QNetworkReply::NetworkError error )
121 {
122  mDebug() << " Error when retrieving OSRM route: " << error;
123 }
124 
125 void OSRMRunner::get()
126 {
127  QNetworkReply *reply = m_networkAccessManager.get( m_request );
128  connect( reply, SIGNAL(error(QNetworkReply::NetworkError)),
129  this, SLOT(handleError(QNetworkReply::NetworkError)), Qt::DirectConnection );
130 }
131 
132 void OSRMRunner::append(QString *input, const QString &key, const QString &value)
133 {
134  *input += '&' + key + '=' + value;
135 }
136 
137 GeoDataLineString *OSRMRunner::decodePolyline( const QString &geometry )
138 {
139  // See https://developers.google.com/maps/documentation/utilities/polylinealgorithm
140  GeoDataLineString* lineString = new GeoDataLineString;
141  int coordinates[2] = { 0, 0 };
142  int const length = geometry.length();
143  for( int i=0; i<length; /* increment happens below */ ) {
144  for ( int j=0; j<2; ++j ) { // lat and lon
145  int block( 0 ), shift( 0 ), result( 0 );
146  do {
147  block = geometry.at( i++ /* increment for outer loop */ ).toLatin1() - 63;
148  result |= ( block & 0x1F ) << shift;
149  shift += 5;
150  } while ( block >= 0x20 );
151  coordinates[j] += ( ( result & 1 ) != 0 ? ~( result >> 1 ) : ( result >> 1 ) );
152  }
153  lineString->append( GeoDataCoordinates( double( coordinates[1] ) / 1E6,
154  double( coordinates[0] ) / 1E6,
155  0.0, GeoDataCoordinates::Degree ) );
156  }
157  return lineString;
158 }
159 
160 RoutingInstruction::TurnType OSRMRunner::parseTurnType( const QString &instruction )
161 {
162  if ( instruction == "1" ) {
163  return RoutingInstruction::Straight;
164  } else if ( instruction == "2" ) {
165  return RoutingInstruction::SlightRight;
166  } else if ( instruction == "3" ) {
167  return RoutingInstruction::Right;
168  } else if ( instruction == "4" ) {
169  return RoutingInstruction::SharpRight;
170  } else if ( instruction == "5" ) {
171  return RoutingInstruction::TurnAround;
172  } else if ( instruction == "6" ) {
173  return RoutingInstruction::SharpLeft;
174  } else if ( instruction == "7" ) {
175  return RoutingInstruction::Left;
176  } else if ( instruction == "8" ) {
177  return RoutingInstruction::SlightLeft;
178  } else if ( instruction == "10" ) {
179  return RoutingInstruction::Continue;
180  } else if ( instruction.startsWith( QLatin1String( "11-" ) ) ) {
181  int const exit = instruction.mid( 3 ).toInt();
182  switch ( exit ) {
183  case 1: return RoutingInstruction::RoundaboutFirstExit; break;
184  case 2: return RoutingInstruction::RoundaboutSecondExit; break;
185  case 3: return RoutingInstruction::RoundaboutThirdExit; break;
186  default: return RoutingInstruction::RoundaboutExit;
187  }
188  } else if ( instruction == "12" ) {
189  return RoutingInstruction::RoundaboutExit;
190  }
191 
192  // ignoring ReachViaPoint = 9;
193  // ignoring StayOnRoundAbout = 13;
194  // ignoring StartAtEndOfStreet = 14;
195  // ignoring ReachedYourDestination = 15;
196 
197  return RoutingInstruction::Unknown;
198 }
199 
200 GeoDataDocument *OSRMRunner::parse( const QByteArray &input ) const
201 {
202  QScriptEngine engine;
203  // Qt requires parentheses around json code
204  QScriptValue const data = engine.evaluate( '(' + QString::fromUtf8( input ) + ')' );
205 
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 );
215 
216  QTime time;
217  time = time.addSecs( data.property( "route_summary" ).property("total_time").toNumber() );
218  qreal length = routeWaypoints->length( EARTH_RADIUS );
219  const QString name = nameString( "OSRM", length, time );
220  const GeoDataExtendedData extendedData = routeData( length, time );
221  routePlacemark->setExtendedData( extendedData );
222  result->setName( name );
223  result->append( routePlacemark );
224  }
225 
226  if ( result && routeWaypoints && data.property( "route_instructions" ).isArray() ) {
227  bool first = true;
228  QScriptValueIterator iterator( data.property( "route_instructions" ) );
229  GeoDataPlacemark* instruction = new GeoDataPlacemark;
230  int lastWaypointIndex = 0;
231  while ( iterator.hasNext() ) {
232  iterator.next();
233  QVariantList details = iterator.value().toVariant().toList();
234  if ( details.size() > 7 ) {
235  QString const text = details.at( 0 ).toString();
236  QString const road = details.at( 1 ).toString();
237  int const waypointIndex = details.at( 3 ).toInt();
238 
239  if ( waypointIndex < routeWaypoints->size() ) {
240  if ( iterator.hasNext() ) {
241  GeoDataLineString *lineString = new GeoDataLineString;
242  for ( int i=lastWaypointIndex; i<=waypointIndex; ++i ) {
243  lineString->append(routeWaypoints->at( i ) );
244  }
245  instruction->setGeometry( lineString );
246  result->append( instruction );
247  instruction = new GeoDataPlacemark;
248  }
249  lastWaypointIndex = waypointIndex;
250  GeoDataExtendedData extendedData;
251  GeoDataData turnTypeData;
252  turnTypeData.setName( "turnType" );
253  RoutingInstruction::TurnType turnType = parseTurnType( text );
254  turnTypeData.setValue( turnType );
255  extendedData.addValue( turnTypeData );
256  if (!road.isEmpty()) {
257  GeoDataData roadName;
258  roadName.setName( "roadName" );
259  roadName.setValue( road );
260  extendedData.addValue( roadName );
261  }
262 
263  if ( first ) {
264  turnType = RoutingInstruction::Continue;
265  first = false;
266  }
267 
268  if ( turnType == RoutingInstruction::Unknown ) {
269  instruction->setName( text );
270  } else {
271  instruction->setName( RoutingInstruction::generateRoadInstruction( turnType, road ) );
272  }
273  instruction->setExtendedData( extendedData );
274 
275  if ( !iterator.hasNext() && lastWaypointIndex > 0 ) {
276  GeoDataLineString *lineString = new GeoDataLineString;
277  for ( int i=lastWaypointIndex; i<waypointIndex; ++i ) {
278  lineString->append(routeWaypoints->at( i ) );
279  }
280  instruction->setGeometry( lineString );
281  result->append( instruction );
282  }
283  }
284  }
285  }
286  }
287 
288  if ( data.property( "hint_data" ).isValid() ) {
289  QVariantList hints = data.property( "hint_data" ).property( "locations" ).toVariant().toList();
290  if ( hints.size() == m_cachedHints.size() ) {
291  for ( int i=0; i<m_cachedHints.size(); ++i ) {
292  m_cachedHints[i].second = hints[i].toString();
293  }
294  }
295 
296  m_hintChecksum = data.property( "hint_data" ).property( "checksum" ).toString();
297  }
298 
299  return result;
300 }
301 
302 } // namespace Marble
303 
304 #include "OSRMRunner.moc"
Marble::GeoDataCoordinates::Unit
Unit
enum used constructor to specify the units used
Definition: GeoDataCoordinates.h:64
GeoDataDocument.h
QTimer::setInterval
void setInterval(int msec)
Marble::RouteRequest::size
int size() const
Number of points in the route.
Definition: RouteRequest.cpp:126
Marble::GeoDataCoordinates
A 3d point representation.
Definition: GeoDataCoordinates.h:52
TinyWebBrowser.h
Marble::GeoDataDocument
A container for Features, Styles and in the future Schemas.
Definition: GeoDataDocument.h:65
QEventLoop
Marble::RoutingInstruction::TurnAround
Definition: RoutingInstruction.h:40
Marble::RoutingInstruction::Right
Definition: RoutingInstruction.h:38
QByteArray
Marble::RoutingInstruction::RoundaboutSecondExit
Definition: RoutingInstruction.h:45
Marble::RoutingInstruction::Left
Definition: RoutingInstruction.h:42
QVariant::toList
QList< QVariant > toList() const
QNetworkReply
QScriptValue
QScriptEngine::evaluate
QScriptValue evaluate(const QString &program, const QString &fileName, int lineNumber)
QScriptValueIterator
GeoDataExtendedData.h
QScriptValue::toVariant
QVariant toVariant() const
Marble::GeoDataCoordinates::latitude
qreal latitude(GeoDataCoordinates::Unit unit=GeoDataCoordinates::Radian) const
retrieves the latitude of the GeoDataCoordinates object use the unit parameter to switch between Radi...
Definition: GeoDataCoordinates.cpp:751
Marble::RoutingInstruction::TurnType
TurnType
Definition: RoutingInstruction.h:32
QTime
MarbleDebug.h
QScriptValue::toString
QString toString() const
Marble::GeoDataCoordinates::Degree
Definition: GeoDataCoordinates.h:66
Maneuver.h
Marble::RoutingRunner::nameString
const QString nameString(const QString &name, qreal length, const QTime &duration) const
Definition: RoutingRunner.cpp:55
Marble::RoutingInstruction::SharpLeft
Definition: RoutingInstruction.h:41
Marble::RoutingInstruction::generateRoadInstruction
static QString generateRoadInstruction(TurnType turnType, const QString &roadName)
Definition: RoutingInstruction.cpp:357
Marble::RouteRequest
Points to be included in a route.
Definition: RouteRequest.h:31
QScriptEngine
QObject::name
const char * name() const
QNetworkRequest
QString::number
QString number(int n, int base)
QString::fromUtf8
QString fromUtf8(const char *str, int size)
Marble::EARTH_RADIUS
const qreal EARTH_RADIUS
Definition: MarbleGlobal.h:257
QTimer
QEventLoop::exec
int exec(QFlags< QEventLoop::ProcessEventsFlag > flags)
Marble::RoutingInstruction::RoundaboutFirstExit
Definition: RoutingInstruction.h:44
Marble::RoutingInstruction::RoundaboutThirdExit
Definition: RoutingInstruction.h:46
Marble::OSRMRunner::~OSRMRunner
~OSRMRunner()
Definition: OSRMRunner.cpp:48
QObject
Marble::RoutingInstruction::Continue
Definition: RoutingInstruction.h:34
QString::toInt
int toInt(bool *ok, int base) const
QNetworkReply::isFinished
bool isFinished() const
QString::isEmpty
bool isEmpty() const
Marble::OSRMRunner::retrieveRoute
virtual void retrieveRoute(const RouteRequest *request)
Start a route download orw calculation.
Definition: OSRMRunner.cpp:53
QString::startsWith
bool startsWith(const QString &s, Qt::CaseSensitivity cs) const
QIODevice::readAll
QByteArray readAll()
QTime::addSecs
QTime addSecs(int s) const
Marble::RoutingInstruction::RoundaboutExit
Definition: RoutingInstruction.h:47
Marble::RoutingRunner::routeData
const GeoDataExtendedData routeData(qreal length, const QTime &duration) const
Definition: RoutingRunner.cpp:61
QObject::deleteLater
void deleteLater()
QString
MarbleLocale.h
GeoDataPlacemark.h
Marble::OSRMRunner::OSRMRunner
OSRMRunner(QObject *parent=0)
Definition: OSRMRunner.cpp:40
QScriptValue::property
QScriptValue property(const QString &name, const ResolveFlags &mode) const
Marble::RoutingInstruction::SlightRight
Definition: RoutingInstruction.h:37
QPair
Marble::RoutingInstruction::SharpRight
Definition: RoutingInstruction.h:39
Marble::GeoDataCoordinates::longitude
qreal longitude(GeoDataCoordinates::Unit unit=GeoDataCoordinates::Radian) const
retrieves the longitude of the GeoDataCoordinates object use the unit parameter to switch between Rad...
Definition: GeoDataCoordinates.cpp:739
QUrl
QChar::toLatin1
char toLatin1() const
Marble::RoutingRunner::routeCalculated
void routeCalculated(GeoDataDocument *route)
Route download/calculation is finished, result in the given route object.
QScriptValue::isString
bool isString() const
OSRMRunner.h
QString::mid
QString mid(int position, int n) const
Marble::RoutingInstruction::Straight
Definition: RoutingInstruction.h:36
QVector
Marble::RoutingRunner
Definition: RoutingRunner.h:28
QLatin1String
QNetworkRequest::setRawHeader
void setRawHeader(const QByteArray &headerName, const QByteArray &headerValue)
Marble::RoutingInstruction::SlightLeft
Definition: RoutingInstruction.h:43
QScriptValue::isValid
bool isValid() const
QString::at
const QChar at(int position) const
QString::length
int length() const
QTimer::start
void start(int msec)
RouteRequest.h
Marble::TinyWebBrowser::userAgent
static QByteArray userAgent(const QString &platform, const QString &plugin)
Definition: TinyWebBrowser.cpp:106
QNetworkAccessManager::get
QNetworkReply * get(const QNetworkRequest &request)
Marble::RoutingInstruction::Unknown
Definition: RoutingInstruction.h:33
QObject::connect
bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
Marble::mDebug
QDebug mDebug()
a function to replace qDebug() in Marble library code
Definition: MarbleDebug.cpp:36
QScriptValue::isArray
bool isArray() const
QScriptValue::toNumber
qsreal toNumber() const
QTimer::setSingleShot
void setSingleShot(bool singleShot)
Marble::RouteRequest::at
GeoDataCoordinates at(int index) const
Accessor for the n-th position.
Definition: RouteRequest.cpp:149
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Mon Jun 22 2020 13:13:41 by doxygen 1.8.7 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

marble

Skip menu "marble"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members
  • Related Pages

kdeedu API Reference

Skip menu "kdeedu API Reference"
  • Analitza
  •     lib
  • kalgebra
  • kalzium
  •   libscience
  • kanagram
  • kig
  •   lib
  • klettres
  • marble
  • parley
  • rocs
  •   App
  •   RocsCore
  •   VisualEditor
  •   stepcore

Search



Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal