• 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
  • openrouteservice
OpenRouteServiceRunner.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 2010 Dennis Nienhüser <earthwings@gentoo.org>
9 //
10 
11 #include "OpenRouteServiceRunner.h"
12 
13 #include "MarbleDebug.h"
14 #include "GeoDataDocument.h"
15 #include "GeoDataPlacemark.h"
16 #include "TinyWebBrowser.h"
17 #include "GeoDataData.h"
18 #include "GeoDataExtendedData.h"
19 #include "routing/RouteRequest.h"
20 
21 #include <QString>
22 #include <QVector>
23 #include <QUrl>
24 #include <QTime>
25 #include <QTimer>
26 #include <QNetworkReply>
27 #include <QDomDocument>
28 
29 namespace Marble
30 {
31 
32 OpenRouteServiceRunner::OpenRouteServiceRunner( QObject *parent ) :
33  RoutingRunner( parent ),
34  m_networkAccessManager()
35 {
36  connect( &m_networkAccessManager, SIGNAL(finished(QNetworkReply*)),
37  this, SLOT(retrieveData(QNetworkReply*)));
38 }
39 
40 OpenRouteServiceRunner::~OpenRouteServiceRunner()
41 {
42  // nothing to do
43 }
44 
45 void OpenRouteServiceRunner::retrieveRoute( const RouteRequest *route )
46 {
47  if ( route->size() < 2 ) {
48  return;
49  }
50 
51  GeoDataCoordinates source = route->source();
52  GeoDataCoordinates destination = route->destination();
53 
54  QHash<QString, QVariant> settings = route->routingProfile().pluginSettings()["openrouteservice"];
55 
56  QString request = xmlHeader();
57  QString unit = "KM";
58  QString preference = "Fastest";
59  if ( settings.contains( "preference" ) ) {
60  preference = settings["preference"].toString();
61  }
62  if ( preference == "Pedestrian" ) {
63  unit = 'M';
64  }
65 
66  request += requestHeader( unit, preference );
67  request += requestPoint( StartPoint, source );
68 
69  if ( route->size() > 2 ) {
70  for ( int i = 1; i < route->size() - 1; ++i ) {
71  request += requestPoint( ViaPoint, route->at( i ) );
72  }
73  }
74 
75  request += requestPoint( EndPoint, destination );
76  request += requestFooter( settings );
77  request += xmlFooter();
78  //mDebug() << "POST: " << request;
79 
80  // Please refrain from making this URI public. To use it outside the scope
81  // of marble you need permission from the openrouteservice.org team.
82  QUrl url = QUrl( "http://openls.geog.uni-heidelberg.de/osm/eu/routing" );
83  m_request = QNetworkRequest( url );
84  m_request.setHeader( QNetworkRequest::ContentTypeHeader, "application/xml" );
85  m_requestData = request.toLatin1();
86 
87  QEventLoop eventLoop;
88  QTimer timer;
89  timer.setSingleShot( true );
90  timer.setInterval( 15000 );
91 
92  connect( &timer, SIGNAL(timeout()),
93  &eventLoop, SLOT(quit()));
94  connect( this, SIGNAL(routeCalculated(GeoDataDocument*)),
95  &eventLoop, SLOT(quit()));
96 
97  // @todo FIXME Must currently be done in the main thread, see bug 257376
98  QTimer::singleShot( 0, this, SLOT(get()));
99  timer.start();
100 
101  eventLoop.exec();
102 }
103 
104 void OpenRouteServiceRunner::get()
105 {
106  QNetworkReply *reply = m_networkAccessManager.post( m_request, m_requestData );
107  connect( reply, SIGNAL(error(QNetworkReply::NetworkError)),
108  this, SLOT(handleError(QNetworkReply::NetworkError)), Qt::DirectConnection);
109 }
110 
111 void OpenRouteServiceRunner::retrieveData( QNetworkReply *reply )
112 {
113  if ( reply->isFinished() ) {
114  QByteArray data = reply->readAll();
115  reply->deleteLater();
116  //mDebug() << "Download completed: " << data;
117  GeoDataDocument* document = parse( data );
118 
119  if ( !document ) {
120  mDebug() << "Failed to parse the downloaded route data" << data;
121  }
122 
123  emit routeCalculated( document );
124  }
125 }
126 
127 void OpenRouteServiceRunner::handleError( QNetworkReply::NetworkError error )
128 {
129  mDebug() << " Error when retrieving openrouteservice.org route: " << error;
130 }
131 
132 QString OpenRouteServiceRunner::xmlHeader() const
133 {
134  QString result = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
135  result += "<xls:XLS xmlns:xls=\"http://www.opengis.net/xls\" xmlns:sch=\"http://www.ascc.net/xml/schematron\" ";
136  result += "xmlns:gml=\"http://www.opengis.net/gml\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" ";
137  result += "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" ";
138  result += "xsi:schemaLocation=\"http://www.opengis.net/xls ";
139  result += "http://schemas.opengis.net/ols/1.1.0/RouteService.xsd\" version=\"1.1\" xls:lang=\"en\">\n";
140  result += "<xls:RequestHeader/>\n";
141  return result;
142 }
143 
144 QString OpenRouteServiceRunner::requestHeader( const QString &unit, const QString &routePreference ) const
145 {
146  QString result = "<xls:Request methodName=\"RouteRequest\" requestID=\"123456789\" version=\"1.1\">\n";
147  result += "<xls:DetermineRouteRequest distanceUnit=\"%1\">\n";
148  result += "<xls:RoutePlan>\n";
149  result += "<xls:RoutePreference>%2</xls:RoutePreference>\n";
150  result += "<xls:WayPointList>\n";
151  return result.arg( unit ).arg( routePreference );
152 }
153 
154 QString OpenRouteServiceRunner::requestPoint( PointType pointType, const GeoDataCoordinates &coordinates ) const
155 {
156  QString result = "<xls:%1>\n";
157  result += "<xls:Position>\n";
158  result += "<gml:Point srsName=\"EPSG:4326\">\n";
159  result += "<gml:pos>%2 %3</gml:pos>\n";
160  result += "</gml:Point>\n";
161  result += "</xls:Position>\n";
162  result += "</xls:%1>\n";
163 
164  result = result.arg( pointType == StartPoint ? "StartPoint" : ( pointType == ViaPoint ? "ViaPoint" : "EndPoint" ) );
165  result = result.arg( coordinates.longitude( GeoDataCoordinates::Degree ), 0, 'f', 14 );
166  result = result.arg( coordinates.latitude( GeoDataCoordinates::Degree ), 0, 'f', 14 );
167  return result;
168 }
169 
170 QString OpenRouteServiceRunner::requestFooter( const QHash<QString, QVariant>& settings ) const
171 {
172  QString result = "</xls:WayPointList>\n";
173 
174  if (settings["noMotorways"].toInt() || settings["noTollways"].toInt() ) {
175  result += "<xls:AvoidList>\n";
176  if ( settings["noTollways"].toInt() ) {
177  result += "<xls:AvoidFeature>Tollway</xls:AvoidFeature>";
178  }
179  if ( settings["noMotorways"].toInt() ) {
180  result += "<xls:AvoidFeature>Highway</xls:AvoidFeature>";
181  }
182  result += "</xls:AvoidList>\n";
183  }
184 
185  result += "</xls:RoutePlan>\n";
186  result += "<xls:RouteInstructionsRequest provideGeometry=\"true\" />\n";
187  result += "<xls:RouteGeometryRequest/>\n";
188  result += "</xls:DetermineRouteRequest>\n";
189  result += "</xls:Request>\n";
190  return result;
191 }
192 
193 QString OpenRouteServiceRunner::xmlFooter() const
194 {
195  return "</xls:XLS>\n";
196 }
197 
198 GeoDataDocument* OpenRouteServiceRunner::parse( const QByteArray &content ) const
199 {
200  QDomDocument xml;
201  if ( !xml.setContent( content ) ) {
202  mDebug() << "Cannot parse xml file with routing instructions.";
203  return 0;
204  }
205 
206  QDomElement root = xml.documentElement();
207 
208  GeoDataDocument* result = new GeoDataDocument();
209  result->setName( "OpenRouteService" );
210 
211  QDomNodeList errors = root.elementsByTagName( "xls:Error" );
212  if ( errors.size() > 0 ) {
213  return 0;
214  // Returning early because fallback routing providers are used now
215  // The code below can be used to parse OpenGis errors reported by ORS
216  // and may be useful in the future
217 
218  for ( unsigned int i = 0; i < errors.length(); ++i ) {
219  QDomNode node = errors.item( i );
220  QString errorMessage = node.attributes().namedItem( "message" ).nodeValue();
221  QRegExp regexp = QRegExp( "^(.*) Please Check your Position: (-?[0-9]+.[0-9]+) (-?[0-9]+.[0-9]+) !" );
222  if ( regexp.indexIn( errorMessage ) == 0 ) {
223  if ( regexp.capturedTexts().size() == 4 ) {
224  GeoDataPlacemark* placemark = new GeoDataPlacemark;
225  placemark->setName( regexp.capturedTexts().at( 1 ) );
226  GeoDataCoordinates position;
227  position.setLongitude( regexp.capturedTexts().at( 2 ).toDouble(), GeoDataCoordinates::Degree );
228  position.setLatitude( regexp.capturedTexts().at( 3 ).toDouble(), GeoDataCoordinates::Degree );
229  placemark->setCoordinate( position );
230  result->append( placemark );
231  }
232  } else {
233  mDebug() << "Error message " << errorMessage << " not parsable.";
235 // QString message = tr( "Sorry, a problem occurred when calculating the route. Try adjusting start and destination points." );
236 // QPointer<QMessageBox> messageBox = new QMessageBox( QMessageBox::Warning, "Route Error", message );
237 // messageBox->setDetailedText( errorMessage );
238 // messageBox->exec();
239 // delete messageBox;
240  }
241  }
242  }
243 
244  GeoDataPlacemark* routePlacemark = new GeoDataPlacemark;
245  routePlacemark->setName( "Route" );
246 
247  QDomNodeList summary = root.elementsByTagName( "xls:RouteSummary" );
248  if ( summary.size() > 0 ) {
249  QDomNodeList time = summary.item( 0 ).toElement().elementsByTagName( "xls:TotalTime" );
250  QDomNodeList distance = summary.item( 0 ).toElement().elementsByTagName( "xls:TotalDistance" );
251  if ( time.size() == 1 && distance.size() == 1 ) {
252  QRegExp regexp = QRegExp( "^P(?:(\\d+)D)?T(?:(\\d+)H)?(?:(\\d+)M)?(\\d+)S" );
253  if ( regexp.indexIn( time.item( 0 ).toElement().text() ) == 0 ) {
254  QStringList matches = regexp.capturedTexts();
255  unsigned int hours( 0 ), minutes( 0 ), seconds( 0 );
256  switch ( matches.size() ) {
257  case 5:
258  // days = regexp.cap( matches.size() - 4 ).toInt();
259  // Intentionally no break
260  case 4:
261  hours = regexp.cap( matches.size() - 3 ).toInt();
262  // Intentionally no break
263  case 3:
264  minutes = regexp.cap( matches.size() - 2 ).toInt();
265  // Intentionally no break
266  case 2:
267  seconds = regexp.cap( matches.size() - 1 ).toInt();
268  break;
269  default:
270  mDebug() << "Unable to parse time string " << time.item( 0 ).toElement().text();
271  }
272 
273  QTime time( hours, minutes, seconds, 0 );
274  qreal totalDistance = distance.item( 0 ).attributes().namedItem( "value" ).nodeValue().toDouble();
275  QString unit = distance.item( 0 ).attributes().namedItem( "uom" ).nodeValue();
276  if ( unit == "M" ) {
277  totalDistance *= METER2KM;
278  }
279  else if ( unit != "KM" ) {
280  mDebug() << "Cannot parse distance unit " << unit << ", treated as km.";
281  }
282 
283  QString description = "";
284  routePlacemark->setDescription( description );
285  }
286  }
287  }
288 
289  GeoDataLineString* routeWaypoints = new GeoDataLineString;
290  QDomNodeList geometry = root.elementsByTagName( "xls:RouteGeometry" );
291  if ( geometry.size() > 0 ) {
292  QDomNodeList waypoints = geometry.item( 0 ).toElement().elementsByTagName( "gml:pos" );
293  for ( unsigned int i = 0; i < waypoints.length(); ++i ) {
294  QDomNode node = waypoints.item( i );
295  QStringList content = node.toElement().text().split( ' ' );
296  if ( content.length() == 2 ) {
297  GeoDataCoordinates position;
298  position.setLongitude( content.at( 0 ).toDouble(), GeoDataCoordinates::Degree );
299  position.setLatitude( content.at( 1 ).toDouble(), GeoDataCoordinates::Degree );
300  routeWaypoints->append( position );
301  }
302  }
303  }
304  routePlacemark->setGeometry( routeWaypoints );
305 
306  QString name = "%1 %2 (OpenRouteService)";
307  QString unit = QLatin1String( "m" );
308  qreal length = routeWaypoints->length( EARTH_RADIUS );
309  if (length >= 1000) {
310  length /= 1000.0;
311  unit = "km";
312  }
313  result->setName( name.arg( length, 0, 'f', 1 ).arg( unit ) );
314 
315  result->append( routePlacemark );
316 
317  QDomNodeList instructionList = root.elementsByTagName( "xls:RouteInstructionsList" );
318  if ( instructionList.size() > 0 ) {
319  QDomNodeList instructions = instructionList.item( 0 ).toElement().elementsByTagName( "xls:RouteInstruction" );
320  for ( unsigned int i = 0; i < instructions.length(); ++i ) {
321  QDomElement node = instructions.item( i ).toElement();
322 
323  QDomNodeList textNodes = node.elementsByTagName( "xls:Instruction" );
324  QDomNodeList positions = node.elementsByTagName( "gml:pos" );
325 
326  if ( textNodes.size() > 0 && positions.size() > 0 ) {
327  QStringList content = positions.at( 0 ).toElement().text().split( ' ' );
328  if ( content.length() == 2 ) {
329  GeoDataLineString *lineString = new GeoDataLineString;
330 
331  for( int i = 0; i < positions.count(); ++i ) {
332  QStringList pointList = positions.at( i ).toElement().text().split( ' ' );
333  GeoDataCoordinates position;
334  position.setLongitude( pointList.at( 0 ).toDouble(), GeoDataCoordinates::Degree );
335  position.setLatitude( pointList.at( 1 ).toDouble(), GeoDataCoordinates::Degree );
336  lineString->append( position );
337  }
338 
339  GeoDataPlacemark* instruction = new GeoDataPlacemark;
340 
341  QString const text = textNodes.item( 0 ).toElement().text();
342  GeoDataExtendedData extendedData;
343  GeoDataData turnTypeData;
344  turnTypeData.setName( "turnType" );
345  QString road;
346  RoutingInstruction::TurnType turnType = parseTurnType( text, &road );
347  turnTypeData.setValue( turnType );
348  extendedData.addValue( turnTypeData );
349  if ( !road.isEmpty() ) {
350  GeoDataData roadName;
351  roadName.setName( "roadName" );
352  roadName.setValue( road );
353  extendedData.addValue( roadName );
354  }
355 
356  QString const instructionText = turnType == RoutingInstruction::Unknown ? text : RoutingInstruction::generateRoadInstruction( turnType, road );
357  instruction->setName( instructionText );
358  instruction->setExtendedData( extendedData );
359  instruction->setGeometry( lineString );
360  result->append( instruction );
361  }
362  }
363  }
364  }
365 
366  return result;
367 }
368 
369 RoutingInstruction::TurnType OpenRouteServiceRunner::parseTurnType( const QString &text, QString *road ) const
370 {
371  QRegExp syntax( "^(Go|Drive) (half left|left|sharp left|straight forward|half right|right|sharp right)( on )?(.*)?$", Qt::CaseSensitive, QRegExp::RegExp2 );
372  QString instruction;
373  if ( syntax.indexIn( text ) == 0 ) {
374  if ( syntax.captureCount() > 1 ) {
375  instruction = syntax.cap( 2 );
376  if ( syntax.captureCount() == 4 ) {
377  *road = syntax.cap( 4 ).remove(QLatin1String( " - Arrived at destination!"));
378  }
379  }
380  }
381 
382  if ( instruction == "Continue" ) {
383  return RoutingInstruction::Straight;
384  } else if ( instruction == "half right" ) {
385  return RoutingInstruction::SlightRight;
386  } else if ( instruction == "right" ) {
387  return RoutingInstruction::Right;
388  } else if ( instruction == "sharp right" ) {
389  return RoutingInstruction::SharpRight;
390  } else if ( instruction == "straight forward" ) {
391  return RoutingInstruction::Straight;
392  } else if ( instruction == "turn" ) {
393  return RoutingInstruction::TurnAround;
394  } else if ( instruction == "sharp left" ) {
395  return RoutingInstruction::SharpLeft;
396  } else if ( instruction == "left" ) {
397  return RoutingInstruction::Left;
398  } else if ( instruction == "half left" ) {
399  return RoutingInstruction::SlightLeft;
400  }
401 
402  return RoutingInstruction::Unknown;
403 }
404 
405 } // namespace Marble
406 
407 #include "OpenRouteServiceRunner.moc"
Marble::OpenRouteServiceRunner::OpenRouteServiceRunner
OpenRouteServiceRunner(QObject *parent=0)
Definition: OpenRouteServiceRunner.cpp:32
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::Left
Definition: RoutingInstruction.h:42
Marble::RouteRequest::destination
GeoDataCoordinates destination() const
The last point, or a default constructed if empty.
Definition: RouteRequest.cpp:140
GeoDataExtendedData.h
Marble::RoutingInstruction::TurnType
TurnType
Definition: RoutingInstruction.h:32
QObject
MarbleDebug.h
Marble::GeoDataCoordinates::Degree
Definition: GeoDataCoordinates.h:66
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::OpenRouteServiceRunner::~OpenRouteServiceRunner
~OpenRouteServiceRunner()
Definition: OpenRouteServiceRunner.cpp:40
Marble::RouteRequest::routingProfile
RoutingProfile routingProfile() const
Definition: RouteRequest.cpp:321
Marble::EARTH_RADIUS
const qreal EARTH_RADIUS
Definition: MarbleGlobal.h:238
OpenRouteServiceRunner.h
GeoDataPlacemark.h
Marble::METER2KM
const qreal METER2KM
Definition: MarbleGlobal.h:205
Marble::OpenRouteServiceRunner::retrieveRoute
virtual void retrieveRoute(const RouteRequest *request)
Start a route download orw calculation.
Definition: OpenRouteServiceRunner.cpp:45
Marble::RoutingInstruction::SlightRight
Definition: RoutingInstruction.h:37
Marble::RoutingInstruction::SharpRight
Definition: RoutingInstruction.h:39
Marble::RoutingProfile::pluginSettings
const QHash< QString, QHash< QString, QVariant > > & pluginSettings() const
Definition: RoutingProfile.cpp:33
Marble::RoutingRunner::routeCalculated
void routeCalculated(GeoDataDocument *route)
Route download/calculation is finished, result in the given route object.
GeoDataData.h
Marble::RoutingInstruction::Straight
Definition: RoutingInstruction.h:36
Marble::RoutingRunner
Definition: RoutingRunner.h:27
Marble::RoutingInstruction::SlightLeft
Definition: RoutingInstruction.h:43
RouteRequest.h
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::source
GeoDataCoordinates source() const
The first point, or a default constructed if empty.
Definition: RouteRequest.cpp:131
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:51 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