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

marble

  • sources
  • kde-4.12
  • 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) const
133 {
134  *input += '&' + key + '=' + value;
135 }
136 
137 GeoDataLineString *OSRMRunner::decodePolyline( const QString &geometry ) const
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 ) const
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 )
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  QString name = "%1 %2 (OSRM)";
217  QString unit = QLatin1String( "m" );
218  qreal length = routeWaypoints->length( EARTH_RADIUS );
219  if (length >= 1000) {
220  length /= 1000.0;
221  unit = "km";
222  }
223  result->setName( name.arg( length, 0, 'f', 1 ).arg( unit ) );
224  result->append( routePlacemark );
225  }
226 
227  if ( result && routeWaypoints && data.property( "route_instructions" ).isArray() ) {
228  bool first = true;
229  QScriptValueIterator iterator( data.property( "route_instructions" ) );
230  GeoDataPlacemark* instruction = new GeoDataPlacemark;
231  int lastWaypointIndex = 0;
232  while ( iterator.hasNext() ) {
233  iterator.next();
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();
239 
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 ) );
245  }
246  instruction->setGeometry( lineString );
247  result->append( instruction );
248  instruction = new GeoDataPlacemark;
249  }
250  lastWaypointIndex = waypointIndex;
251  GeoDataExtendedData extendedData;
252  GeoDataData turnTypeData;
253  turnTypeData.setName( "turnType" );
254  RoutingInstruction::TurnType turnType = parseTurnType( text );
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 );
262  }
263 
264  if ( first ) {
265  turnType = RoutingInstruction::Continue;
266  first = false;
267  }
268 
269  if ( turnType == RoutingInstruction::Unknown ) {
270  instruction->setName( text );
271  } else {
272  instruction->setName( RoutingInstruction::generateRoadInstruction( turnType, road ) );
273  }
274  instruction->setExtendedData( extendedData );
275 
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 ) );
280  }
281  instruction->setGeometry( lineString );
282  result->append( instruction );
283  }
284  }
285  }
286  }
287  }
288 
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();
294  }
295  }
296 
297  m_hintChecksum = data.property( "hint_data" ).property( "checksum" ).toString();
298  }
299 
300  return result;
301 }
302 
303 } // namespace Marble
304 
305 #include "OSRMRunner.moc"
Marble::GeoDataCoordinates::Unit
Unit
enum used constructor to specify the units used
Definition: GeoDataCoordinates.h:64
GeoDataDocument.h
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:64
Marble::RoutingInstruction::TurnAround
Definition: RoutingInstruction.h:40
Marble::RoutingInstruction::Right
Definition: RoutingInstruction.h:38
Marble::RoutingInstruction::RoundaboutSecondExit
Definition: RoutingInstruction.h:45
Marble::RoutingInstruction::Left
Definition: RoutingInstruction.h:42
GeoDataExtendedData.h
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
QObject
MarbleDebug.h
Marble::GeoDataCoordinates::Degree
Definition: GeoDataCoordinates.h:66
Maneuver.h
Marble::RoutingInstruction::SharpLeft
Definition: RoutingInstruction.h:41
Marble::RoutingInstruction::generateRoadInstruction
static QString generateRoadInstruction(TurnType turnType, const QString &roadName)
Definition: RoutingInstruction.cpp:351
Marble::RouteRequest
Points to be included in a route.
Definition: RouteRequest.h:31
Marble::EARTH_RADIUS
const qreal EARTH_RADIUS
Definition: MarbleGlobal.h:238
Marble::RoutingInstruction::RoundaboutFirstExit
Definition: RoutingInstruction.h:44
Marble::RoutingInstruction::RoundaboutThirdExit
Definition: RoutingInstruction.h:46
Marble::OSRMRunner::~OSRMRunner
~OSRMRunner()
Definition: OSRMRunner.cpp:48
Marble::RoutingInstruction::Continue
Definition: RoutingInstruction.h:34
Marble::OSRMRunner::retrieveRoute
virtual void retrieveRoute(const RouteRequest *request)
Start a route download orw calculation.
Definition: OSRMRunner.cpp:53
Marble::RoutingInstruction::RoundaboutExit
Definition: RoutingInstruction.h:47
MarbleLocale.h
GeoDataPlacemark.h
Marble::OSRMRunner::OSRMRunner
OSRMRunner(QObject *parent=0)
Definition: OSRMRunner.cpp:40
Marble::RoutingInstruction::SlightRight
Definition: RoutingInstruction.h:37
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
Marble::RoutingRunner::routeCalculated
void routeCalculated(GeoDataDocument *route)
Route download/calculation is finished, result in the given route object.
OSRMRunner.h
Marble::RoutingInstruction::Straight
Definition: RoutingInstruction.h:36
Marble::RoutingRunner
Definition: RoutingRunner.h:27
Marble::RoutingInstruction::SlightLeft
Definition: RoutingInstruction.h:43
RouteRequest.h
Marble::TinyWebBrowser::userAgent
static QByteArray userAgent(const QString &platform, const QString &plugin)
Definition: TinyWebBrowser.cpp:106
Marble::RoutingInstruction::Unknown
Definition: RoutingInstruction.h:33
Marble::mDebug
QDebug mDebug()
a function to replace qDebug() in Marble library code
Definition: MarbleDebug.cpp:31
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-2014 The KDE developers.
Generated on Tue Oct 14 2014 22:38:52 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
  • kstars
  • libkdeedu
  •   keduvocdocument
  • 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