Marble

RoutingInstruction.cpp
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 <[email protected]>
9 //
10 
11 #include "RoutingInstruction.h"
12 
13 #include <QCoreApplication>
14 #include <QStringList>
15 #include <QTextStream>
16 
17 #include <cmath>
18 
19 namespace Marble
20 {
21 
23  m_roadName( item.roadName() ), m_roadType( item.roadType() ),
24  m_secondsLeft( item.secondsRemaining() ),
25  m_angleToPredecessor( 0.0 ), m_roundaboutExit( 0 ),
26  m_predecessor( nullptr ), m_successor( nullptr )
27 {
28  m_points.append( item );
29 }
30 
31 bool RoutingInstruction::append( const RoutingWaypoint &item, int angle )
32 {
33  if (m_points.size() &&
34  m_points.last().roadType() != QLatin1String("roundabout") &&
35  item.roadType() == QLatin1String("roundabout")) {
36  // Entering a roundabout. Merge with previous segment to avoid 'Enter the roundabout' instructions
37  m_points.push_back( item );
38  return true;
39  }
40 
41  if (m_points.size() &&
42  m_points.last().roadType() == QLatin1String("roundabout") &&
43  item.roadType() != QLatin1String("roundabout")) {
44  // Exiting a roundabout
45  m_points.push_back( item );
46  return false;
47  }
48 
49  m_points.push_back( item );
50 
51  if ( item.junctionType() == RoutingWaypoint::Roundabout ) {
52  // Passing a roundabout exit
53  ++m_roundaboutExit;
54  return true;
55  }
56 
57  if ( item.roadName().isEmpty() ) {
58  if ( item.junctionType() == RoutingWaypoint::None ) {
59  return true;
60  }
61 
62  return angle >= 150 && angle <= 210;
63  } else {
64  return item.roadType() == QLatin1String("roundabout") || item.roadName() == roadName();
65  }
66 }
67 
69 {
70  return m_roadName;
71 }
72 
74 {
75  return m_roadType;
76 }
77 
79 {
80  return m_secondsLeft;
81 }
82 
83 void RoutingInstruction::calculateAngle()
84 {
85  if ( !m_predecessor ) {
86  return;
87  }
88 
89  int hisSize = m_predecessor->points().size();
90  int mySize = m_points.size();
91  Q_ASSERT( mySize > 0 && hisSize > 0 );
92  RoutingPoint one = points().first().point();
93  RoutingPoint two = m_predecessor->points().at( hisSize - 1 ).point();
94  qreal distance = 0;
95  for ( int i = 2; i <= qMin<int>( hisSize, 20 ) && distance < 50.0; ++i ) {
96  two = m_predecessor->points().at( hisSize - i ).point();
97  m_intersectionPoints.push_front( two );
98  distance = one.distance( two );
99  }
100  qreal before = two.bearing( one );
101  m_intersectionPoints.push_back( one );
102 
103  one = points().first().point();
104  if ( mySize == 1 && !m_successor ) {
105  return;
106  } else if ( mySize == 1 ) {
107  Q_ASSERT( !m_successor->points().isEmpty() );
108  two = m_successor->points().first().point();
109  } else {
110  two = points().at( 1 ).point();
111  }
112 
113  distance = 0;
114  m_intersectionPoints.push_back( one );
115  for ( int i = 2; i < qMin<int>( mySize, 20 ) && distance < 50.0; ++i ) {
116  two = points().at( i ).point();
117  m_intersectionPoints.push_back( two );
118  distance = one.distance( two );
119  }
120 
121  qreal after = one.bearing( two );
122  m_angleToPredecessor = after - before;
123 }
124 
125 void RoutingInstruction::calculateTurnType()
126 {
127  if ( predecessor() && predecessor()->roundaboutExitNumber() ) {
128  int exit = predecessor()->roundaboutExitNumber();
129  switch( exit ) {
130  case 1:
131  m_turnType = RoundaboutFirstExit;
132  break;
133  case 2:
134  m_turnType = RoundaboutSecondExit;
135  break;
136  case 3:
137  m_turnType = RoundaboutThirdExit;
138  break;
139  default:
140  m_turnType = RoundaboutExit;
141  break;
142  }
143 
144  return;
145  }
146 
147  int angle = qRound( angleToPredecssor() * 180.0 / M_PI + 540 ) % 360;
148  Q_ASSERT( angle >= 0 && angle <= 360 );
149 
150  const int sharp = 30;
151  if ( angle >= 360 - sharp || angle < sharp ) {
152  m_turnType = TurnAround;
153  } else if ( angle >= sharp && angle < 90 - sharp ) {
154  m_turnType = SharpLeft;
155  } else if ( angle >= 90 - sharp && angle < 90 + sharp ) {
156  m_turnType = Left;
157  } else if ( angle >= 90 + sharp && angle < 180 - sharp ) {
158  m_turnType = SlightLeft;
159  } else if ( angle >= 180 - sharp && angle < 180 + sharp ) {
160  m_turnType = Straight;
161  } else if ( angle >= 180 + sharp && angle < 270 - sharp ) {
162  m_turnType = SlightRight;
163  } else if ( angle >= 270 - sharp && angle < 270 + sharp ) {
164  m_turnType = Right;
165  } else if ( angle >= 270 + sharp && angle < 360 - sharp ) {
166  m_turnType = SharpRight;
167  } else {
168  Q_ASSERT( false && "Internal error: not all angles are properly handled" );
169  }
170 }
171 
173 {
174  return m_points;
175 }
176 
178 {
179  return m_intersectionPoints;
180 }
181 
183 {
184  return m_angleToPredecessor;
185 }
186 
188 {
189  return m_predecessor;
190 }
191 
193 {
194  return m_predecessor;
195 }
196 
198 {
199  m_predecessor = predecessor;
200  calculateAngle();
201  calculateTurnType();
202 }
203 
205 {
206  return m_successor;
207 }
208 
210 {
211  return m_successor;
212 }
213 
215 {
216  m_successor = successor;
217 }
218 
220 {
221  qreal result = 0.0;
222  for ( int i = 1; i < m_points.size(); ++i ) {
223  result += m_points[i-1].point().distance( m_points[i].point() );
224  }
225 
226  return result;
227 }
228 
230 {
231  qreal result = 0.0;
232  const RoutingInstruction* i = predecessor();
233  while ( i ) {
234  result += i->distance();
235  i = i->predecessor();
236  }
237  return result;
238 }
239 
241 {
242  qreal result = distance();
243  const RoutingInstruction* i = successor();
244  while ( i ) {
245  result += i->distance();
246  i = i->successor();
247  }
248  return result;
249 }
250 
252 {
253  if (roadType() == QLatin1String("roundabout")) {
254  return QObject::tr( "Enter the roundabout." );
255  }
256 
257  if (roadType() == QLatin1String("motorway_link")) {
258  QStringList motorways = QStringList() << "motorway" << "motorway_link";
259  bool const leaving = predecessor() && motorways.contains( predecessor()->roadType() );
260  if ( leaving ) {
261  if ( roadName().isEmpty() ) {
262  return QObject::tr( "Take the exit." );
263  } else {
264  return QObject::tr( "Take the exit towards %1." ).arg( roadName() );
265  }
266  }
267  if ( roadName().isEmpty() ) {
268  return QObject::tr( "Take the ramp." );
269  } else {
270  return QObject::tr( "Take the ramp towards %1." ).arg( roadName() );
271  }
272  }
273 
274  TurnType turnType = m_turnType;
275  if ( predecessor() && predecessor()->roundaboutExitNumber() ) {
276  switch ( predecessor()->roundaboutExitNumber() ) {
277  case 1:
278  turnType = RoundaboutFirstExit;
279  break;
280  case 2:
281  turnType = RoundaboutSecondExit;
282  break;
283  case 3:
284  turnType = RoundaboutThirdExit;
285  break;
286  }
287  }
288 
289  return generateRoadInstruction( turnType, roadName() );
290 }
291 
293 {
294  QLocale::MeasurementSystem const measurement = QLocale::system().measurementSystem();
295  int precision = 0;
296  qreal length = distance();
297  QString distanceUnit = QLatin1String( "m" );
298 
299  if ( measurement != QLocale::MetricSystem ) {
300  precision = 1;
301  distanceUnit = "mi";
302  length /= 1000.0;
303  length /= 1.609344;
304  if ( length < 0.1 ) {
305  length = 10 * qRound( length * 528 );
306  precision = 0;
307  distanceUnit = "ft";
308  }
309  } else {
310  if ( length >= 1000 ) {
311  length /= 1000;
312  distanceUnit = "km";
313  precision = 1;
314  } else if ( length >= 200 ) {
315  length = 50 * qRound( length / 50 );
316  } else if ( length >= 100 ) {
317  length = 25 * qRound( length / 25 );
318  } else {
319  length = 10 * qRound( length / 10 );
320  }
321  }
322 
323  if ( length == 0 ) {
324  return QString();
325  } else {
326  QString text = QObject::tr( "Follow the road for %1 %2." );
327  return text.arg( length, 0, 'f', precision ).arg( distanceUnit );
328  }
329 }
330 
332 {
333  qreal duration = secondsLeft();
334  QString durationUnit = "sec";
335  int precision = 0;
336  if ( duration >= 60.0 ) {
337  duration /= 60.0;
338  durationUnit = "min";
339  precision = 0;
340  }
341  if ( duration >= 60.0 ) {
342  duration /= 60.0;
343  durationUnit = QStringLiteral("h");
344  precision = 1;
345  }
346 
347  QString text = "Arrival in %1 %2.";
348  return text.arg( duration, 0, 'f', precision ).arg( durationUnit );
349 }
350 
352 {
353  QString text = nextRoadInstruction();
354  text += QLatin1Char(' ') + nextDistanceInstruction();
355  if (QCoreApplication::instance()->arguments().contains(QStringLiteral("--remaining-duration"))) {
356  text += QLatin1Char(' ') + totalDurationRemaining();
357  }
358  return text;
359 }
360 
361 QString RoutingInstruction::generateRoadInstruction( RoutingInstruction::TurnType turnType, const QString &roadName )
362 {
363  int roundaboutExit = 0;
364  switch ( turnType ) {
365  case RoundaboutFirstExit:
366  roundaboutExit = 1;
367  break;
368  case RoundaboutSecondExit:
369  roundaboutExit = 2;
370  break;
371  case RoundaboutThirdExit:
372  roundaboutExit = 3;
373  break;
374  default:
375  break;
376  }
377 
378  if ( roundaboutExit > 0 ) {
379  if ( roadName.isEmpty() ) {
380  return QObject::tr( "Take the %1. exit in the roundabout." ).arg( roundaboutExit ); // One sentence
381  } else {
382  QString text = QObject::tr( "Take the %1. exit in the roundabout into %2." ); // One sentence
383  return text.arg( roundaboutExit ).arg( roadName );
384  }
385  }
386 
387  if ( roadName.isEmpty() ) {
388  switch( turnType ) {
389  case Continue:
390  return QObject::tr( "Continue." );
391  case Merge:
392  return QObject::tr( "Merge." );
393  case TurnAround:
394  return QObject::tr( "Turn around." );
395  case SharpLeft:
396  return QObject::tr( "Turn sharp left." );
397  case Left:
398  return QObject::tr( "Turn left." );
399  case SlightLeft:
400  return QObject::tr( "Keep slightly left." );
401  case Straight:
402  return QObject::tr( "Go straight ahead." );
403  case SlightRight:
404  return QObject::tr( "Keep slightly right." );
405  case Right:
406  return QObject::tr( "Turn right." );
407  case SharpRight:
408  return QObject::tr( "Turn sharp right." );
409  case RoundaboutExit:
410  return QObject::tr( "Exit the roundabout." );
411  case Unknown:
412  case RoundaboutFirstExit:
413  case RoundaboutSecondExit:
414  case RoundaboutThirdExit:
415  Q_ASSERT( false && "Internal error: Unknown/Roundabout should have been handled earlier." );
416  return QString();
417  case ExitLeft:
418  return QObject::tr( "Take the exit to the left." );
419  case ExitRight:
420  return QObject::tr( "Take the exit to the right." );
421  }
422  } else {
423  switch( turnType ) {
424  case Continue:
425  return QObject::tr( "Continue onto %1." ).arg( roadName );
426  case Merge:
427  return QObject::tr( "Merge onto %1." ).arg( roadName );
428  case TurnAround:
429  return QObject::tr( "Turn around onto %1." ).arg( roadName );
430  case SharpLeft:
431  return QObject::tr( "Turn sharp left on %1." ).arg( roadName );
432  case Left:
433  return QObject::tr( "Turn left into %1." ).arg( roadName );
434  case SlightLeft:
435  return QObject::tr( "Keep slightly left on %1." ).arg( roadName );
436  case Straight:
437  return QObject::tr( "Continue on %1." ).arg( roadName );
438  case SlightRight:
439  return QObject::tr( "Keep slightly right on %1." ).arg( roadName );
440  case Right:
441  return QObject::tr( "Turn right into %1." ).arg( roadName );
442  case SharpRight:
443  return QObject::tr( "Turn sharp right into %1." ).arg( roadName );
444  case RoundaboutExit:
445  return QObject::tr( "Exit the roundabout into %2." ).arg( roadName );
446  case Unknown:
447  case RoundaboutFirstExit:
448  case RoundaboutSecondExit:
449  case RoundaboutThirdExit:
450  Q_ASSERT( false && "Internal error: Unknown/Roundabout should have been handled earlier." );
451  return QString();
452  case ExitLeft:
453  return QObject::tr( "Take the exit to the left onto %1." ).arg( roadName );
454  case ExitRight:
455  return QObject::tr( "Take the exit to the right onto %1." ).arg( roadName );
456  }
457  }
458 
459  Q_ASSERT( false && "Internal error: Switch did not handle all cases.");
460  return QString();
461 }
462 
464 {
465  stream.setRealNumberPrecision( 8 );
466  if ( i.points().isEmpty() ) {
467  return stream;
468  }
469 
470  if (QCoreApplication::instance()->arguments().contains(QStringLiteral("--dense"))) {
472  int maxElement = points.size() - ( i.successor() ? 1 : 0 );
473  for ( int j = 0; j < maxElement; ++j ) {
474  stream << points[j].point().lat() << ',';
475  stream << points[j].point().lon() << ',';
476  stream << points[j].junctionTypeRaw() << ',';
477  stream << points[j].roadType() << ',';
478  stream << points[j].secondsRemaining() << ',';
479  if ( !j ) {
480  stream << i.instructionText();
481  }
482  if ( j < maxElement - 1 ) {
483  stream << '\n';
484  }
485  }
486 
487  return stream;
488  }
489 
490  if (QCoreApplication::instance()->arguments().contains(QStringLiteral("--csv"))) {
491  stream << i.points().first().point().lat() << ',';
492  stream << i.points().first().point().lon() << ',';
493  } else {
494  QString distanceUnit = "m ";
495  int precision = 0;
496  qreal length = i.distanceFromStart();
497  if ( length >= 1000 ) {
498  length /= 1000;
499  distanceUnit = "km";
500  precision = 1;
501  }
502 
503  QString totalDistance = "[%1 %2] ";
504  stream << totalDistance.arg( length, 3, 'f', precision ).arg( distanceUnit );
505  }
506 
507  stream << i.instructionText();
508 
509  if (QCoreApplication::instance()->arguments().contains(QStringLiteral("--csv")) &&
510  QCoreApplication::instance()->arguments().contains(QStringLiteral("--intersection-points"))) {
511  for ( const RoutingPoint &point: i.intersectionPoints() ) {
512  stream << ',' << point.lat() << ',' << point.lon();
513  }
514  }
515 
516  return stream;
517 }
518 
519 int RoutingInstruction::roundaboutExitNumber() const
520 {
521  return m_roundaboutExit;
522 }
523 
524 RoutingInstruction::TurnType RoutingInstruction::turnType() const
525 {
526  return m_turnType;
527 }
528 
529 } // namespace Marble
Stores data related to one instruction: Road name, angle to predecessor, associated waypoints etc...
KCALENDARCORE_EXPORT QDataStream & operator<<(QDataStream &out, const KCalendarCore::Alarm::Ptr &)
QString roadName() const
OSM name of the road.
qreal distanceFromStart() const
The distance from the route start.
RoutingInstruction(const RoutingWaypoint &item=RoutingWaypoint())
Constructor.
bool append(const RoutingWaypoint &item, int angle)
Append data of the given item, returns true if item&#39;s street name matches instructions street name...
void setRealNumberPrecision(int precision)
Binds a QML item to a specific geodetic location in screen coordinates.
bool contains(const QString &str, Qt::CaseSensitivity cs) const const
QLocale::MeasurementSystem measurementSystem() const const
QString tr(const char *sourceText, const char *disambiguation, int n)
void setPredecessor(RoutingInstruction *predecessor)
Change the predecessor.
qreal angleToPredecssor() const
The angle between the two turn roads, in radians.
QLocale system()
QString roadType() const
OSM type of the road to turn into.
qreal distance(const RoutingPoint &other) const
Calculates the distance in meter between this point and the given other point.
JunctionType junctionType() const
Parsed junction type.
qreal distanceToEnd() const
The distance to the route end.
bool isEmpty() const const
void setSuccessor(RoutingInstruction *successor)
Change the successor.
QString totalDurationRemaining() const
Formats the instruction (duration to destination) for a human reader.
QCoreApplication * instance()
QVector< RoutingPoint > intersectionPoints() const
Contains the intersection point and points near it on the previous and current road.
QString roadName() const
Name of the road to turn into.
QVector< RoutingWaypoint > points() const
Waypoints from the last instruction to this instruction.
QString nextDistanceInstruction() const
Formats the instruction (distance to next instruction) for a human reader.
RoutingInstruction * predecessor()
Previous turn road.
QString nextRoadInstruction() const
Formats the instruction (road name) for a human reader.
int secondsLeft() const
Estimated number of seconds to the route destination.
RoutingInstruction * successor()
Next turn road.
QString roadType() const
OSM type of the road.
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
There are many Point classes, but this is mine.
Definition: RoutingPoint.h:26
qreal bearing(const RoutingPoint &other) const
Calculates the bearing of the line defined by this point and the given other point.
QString instructionText() const
Formats the instruction for a human reader.
qreal distance() const
The accumulated distance of all waypoints belonging to this instruction.
QStringList arguments()
int size() const const
Stores one line of gosmore/routino output.
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Fri Jun 5 2020 22:32:25 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.