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