• 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
  • lib
  • marble
  • routing
AlternativeRoutesModel.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 "AlternativeRoutesModel.h"
12 
13 #include "GeoDataDocument.h"
14 #include "GeoDataFolder.h"
15 #include "GeoDataExtendedData.h"
16 #include "GeoDataPlacemark.h"
17 #include "MarbleMath.h"
18 #include "RoutingModel.h"
19 
20 #include <QTimer>
21 #include <QPainter>
22 
23 namespace Marble {
24 
25 class AlternativeRoutesModelPrivate
26 {
27 public:
29  QVector<GeoDataDocument*> m_routes;
30 
32  QVector<GeoDataDocument*> m_restrainedRoutes;
33 
35  QTime m_responseTime;
36 
37  int m_currentIndex;
38 
39  AlternativeRoutesModelPrivate();
40 
44  bool filter( const GeoDataDocument* document ) const;
45 
53  static qreal similarity( const GeoDataDocument* routeA, const GeoDataDocument* routeB );
54 
58  static qreal distance( const GeoDataLineString* wayPoints, const GeoDataCoordinates &position );
59 
64  static qreal bearing( const GeoDataCoordinates &one, const GeoDataCoordinates &two );
65 
70  static qreal distance( const GeoDataCoordinates &satellite, const GeoDataCoordinates &lineA, const GeoDataCoordinates &lineB );
71 
75  static GeoDataCoordinates coordinates( const GeoDataCoordinates &start, qreal distance, qreal bearing );
76 
81  static qreal unidirectionalSimilarity( const GeoDataDocument* routeA, const GeoDataDocument* routeB );
82 
86  static bool higherScore( const GeoDataDocument* one, const GeoDataDocument* two );
87 
91  static qreal instructionScore( const GeoDataDocument* document );
92 
93  static const GeoDataLineString* waypoints( const GeoDataDocument* document );
94 
95  static int nonZero( const QImage &image );
96 
97  static QPolygonF polygon( const GeoDataLineString* lineString, qreal x, qreal y, qreal sx, qreal sy );
98 };
99 
100 
101 AlternativeRoutesModelPrivate::AlternativeRoutesModelPrivate() :
102  m_currentIndex( -1 )
103 {
104  // nothing to do
105 }
106 
107 int AlternativeRoutesModelPrivate::nonZero( const QImage &image )
108 {
109  QRgb const black = qRgb( 0, 0, 0 );
110  int count = 0;
111  for ( int y = 0; y < image.height(); ++y ) {
112  QRgb* destLine = (QRgb*) image.scanLine( y );
113  for ( int x = 0; x < image.width(); ++x ) {
114  count += destLine[x] == black ? 0 : 1;
115  }
116  }
117  return count;
118 }
119 
120 QPolygonF AlternativeRoutesModelPrivate::polygon( const GeoDataLineString* lineString, qreal x, qreal y, qreal sx, qreal sy )
121 {
122  QPolygonF poly;
123  for ( int i=0; i<lineString->size(); ++i ) {
124  poly << QPointF( qAbs( ( *lineString)[i].longitude() - x ) * sx,
125  qAbs( ( *lineString)[i].latitude() - y ) * sy );
126  }
127  return poly;
128 }
129 
130 bool AlternativeRoutesModelPrivate::filter( const GeoDataDocument* document ) const
131 {
132  for ( int i=0; i<m_routes.size(); ++i ) {
133  qreal similarity = AlternativeRoutesModelPrivate::similarity( document, m_routes.at( i ) );
134  if ( similarity > 0.8 ) {
135  return true;
136  }
137  }
138 
139  return false;
140 }
141 
142 qreal AlternativeRoutesModelPrivate::similarity( const GeoDataDocument* routeA, const GeoDataDocument* routeB )
143 {
144  return qMax<qreal>( unidirectionalSimilarity( routeA, routeB ),
145  unidirectionalSimilarity( routeB, routeA ) );
146 }
147 
148 qreal AlternativeRoutesModelPrivate::distance( const GeoDataLineString* wayPoints, const GeoDataCoordinates &position )
149 {
150  Q_ASSERT( wayPoints && !wayPoints->isEmpty() );
151  qreal minDistance = 0;
152  for ( int i=1; i<wayPoints->size(); ++i ) {
153  qreal dist = distance( position, wayPoints->at( i-1 ), wayPoints->at( i ) );
154  if ( minDistance <= 0 || dist < minDistance ) {
155  minDistance = dist;
156  }
157  }
158 
159  return minDistance;
160 }
161 
162 qreal AlternativeRoutesModelPrivate::bearing( const GeoDataCoordinates &one, const GeoDataCoordinates &two )
163 {
164  qreal delta = two.longitude() - one.longitude();
165  qreal lat1 = one.latitude();
166  qreal lat2 = two.latitude();
167  return fmod( atan2( sin ( delta ) * cos ( lat2 ),
168  cos( lat1 ) * sin( lat2 ) - sin( lat1 ) * cos( lat2 ) * cos ( delta ) ), 2 * M_PI );
169 }
170 
171 GeoDataCoordinates AlternativeRoutesModelPrivate::coordinates( const GeoDataCoordinates &start, qreal distance, qreal bearing )
172 {
173  qreal lat1 = start.latitude();
174  qreal lon1 = start.longitude();
175  qreal lat2 = asin( sin( lat1 ) * cos( distance ) + cos( lat1 ) * sin( distance ) * cos( bearing ) );
176  qreal lon2 = lon1 + atan2( sin( bearing ) * sin( distance ) * cos( lat1 ), cos( distance ) - sin( lat1 ) * sin( lat2 ) );
177  return GeoDataCoordinates( lon2, lat2 );
178 }
179 
180 qreal AlternativeRoutesModelPrivate::distance( const GeoDataCoordinates &satellite, const GeoDataCoordinates &lineA, const GeoDataCoordinates &lineB )
181 {
182  qreal dist = distanceSphere( lineA, satellite );
183  qreal bearA = bearing( lineA, satellite );
184  qreal bearB = bearing( lineA, lineB );
185  qreal result = asin( sin ( dist ) * sin( bearB - bearA ) );
186  Q_ASSERT( qMax<qreal>( distanceSphere(satellite, lineA), distanceSphere(satellite, lineB) ) >= qAbs<qreal>(result) );
187 
188  result = acos( cos( dist ) / cos( result ) );
190  qreal final = qMin<qreal>( distanceSphere( satellite, lineA ), distanceSphere( satellite, lineB ) );
191  if ( result >= 0 && result <= distanceSphere( lineA, lineB ) ) {
192  GeoDataCoordinates nearest = coordinates( lineA, result, bearB );
193  return qMin<qreal>( final, distanceSphere( satellite, nearest ) );
194  } else {
195  return final;
196  }
197 }
198 
199 qreal AlternativeRoutesModelPrivate::unidirectionalSimilarity( const GeoDataDocument* routeA, const GeoDataDocument* routeB )
200 {
201  const GeoDataLineString* waypointsA = waypoints( routeA );
202  const GeoDataLineString* waypointsB = waypoints( routeB );
203  if ( !waypointsA || !waypointsB )
204  {
205  return 0.0;
206  }
207 
208  QImage image( 64, 64, QImage::Format_ARGB32_Premultiplied );
209  image.fill( qRgb( 0, 0, 0 ) );
210  GeoDataLatLonBox box = GeoDataLatLonBox::fromLineString( *waypointsA );
211  box = box.united( GeoDataLatLonBox::fromLineString( *waypointsB ) );
212  if ( !box.width() || !box.height() ) {
213  return 0.0;
214  }
215 
216  qreal const sw = image.width() / box.width();
217  qreal const sh = image.height() / box.height();
218 
219  QPainter painter( &image );
220  painter.setPen( QColor( Qt::white ) );
221 
222  painter.drawPoints( AlternativeRoutesModelPrivate::polygon( waypointsA, box.west(), box.north(), sw, sh ) );
223  int const countA = AlternativeRoutesModelPrivate::nonZero( image );
224 
225  painter.drawPoints( AlternativeRoutesModelPrivate::polygon( waypointsB, box.west(), box.north(), sw, sh ) );
226  int const countB = AlternativeRoutesModelPrivate::nonZero( image );
227  Q_ASSERT( countA <= countB );
228  return countB ? 1.0 - qreal( countB - countA ) / countB : 0;
229 }
230 
231 bool AlternativeRoutesModelPrivate::higherScore( const GeoDataDocument* one, const GeoDataDocument* two )
232 {
233  qreal instructionScoreA = instructionScore( one );
234  qreal instructionScoreB = instructionScore( two );
235  if ( instructionScoreA != instructionScoreB ) {
236  return instructionScoreA > instructionScoreB;
237  }
238 
239  qreal lengthA = waypoints( one )->length( EARTH_RADIUS );
240  qreal lengthB = waypoints( two )->length( EARTH_RADIUS );
241 
242  return lengthA < lengthB;
243 }
244 
245 qreal AlternativeRoutesModelPrivate::instructionScore( const GeoDataDocument* document )
246 {
247  bool hasInstructions = false;
248 
249  QStringList blacklist = QStringList() << "" << "Route" << "Tessellated";
250  QVector<GeoDataFolder*> folders = document->folderList();
251  foreach( const GeoDataFolder *folder, folders ) {
252  foreach( const GeoDataPlacemark *placemark, folder->placemarkList() ) {
253  if ( !blacklist.contains( placemark->name() ) ) {
254  hasInstructions = true;
255  break;
256  }
257  }
258  }
259 
260  foreach( const GeoDataPlacemark *placemark, document->placemarkList() ) {
261  if ( !blacklist.contains( placemark->name() ) ) {
262  hasInstructions = true;
263 
264  if ( placemark->extendedData().contains( "turnType" ) ) {
265  return 1.0;
266  }
267  }
268  }
269 
270  return hasInstructions ? 0.5 : 0.0;
271 }
272 
273 const GeoDataLineString* AlternativeRoutesModelPrivate::waypoints( const GeoDataDocument* document )
274 {
275  QVector<GeoDataFolder*> folders = document->folderList();
276  foreach( const GeoDataFolder *folder, folders ) {
277  foreach( const GeoDataPlacemark *placemark, folder->placemarkList() ) {
278  const GeoDataGeometry* geometry = placemark->geometry();
279  const GeoDataLineString* lineString = dynamic_cast<const GeoDataLineString*>( geometry );
280  if ( lineString ) {
281  return lineString;
282  }
283  }
284  }
285 
286  foreach( const GeoDataPlacemark *placemark, document->placemarkList() ) {
287  const GeoDataGeometry* geometry = placemark->geometry();
288  const GeoDataLineString* lineString = dynamic_cast<const GeoDataLineString*>( geometry );
289  if ( lineString ) {
290  return lineString;
291  }
292  }
293 
294  return 0;
295 }
296 
297 AlternativeRoutesModel::AlternativeRoutesModel( QObject *parent ) :
298  QAbstractListModel( parent ),
299  d( new AlternativeRoutesModelPrivate() )
300 {
301  // nothing to do
302 }
303 
304 AlternativeRoutesModel::~AlternativeRoutesModel()
305 {
306  delete d;
307 }
308 
309 int AlternativeRoutesModel::rowCount ( const QModelIndex & ) const
310 {
311  return d->m_routes.size();
312 }
313 
314 QVariant AlternativeRoutesModel::headerData ( int, Qt::Orientation, int ) const
315 {
316  return QVariant();
317 }
318 
319 QVariant AlternativeRoutesModel::data ( const QModelIndex &index, int role ) const
320 {
321  QVariant result;
322 
323  if ( role == Qt::DisplayRole && index.column() == 0 && index.row() >= 0 && index.row() < d->m_routes.size() ) {
324  result = d->m_routes.at( index.row() )->name();
325  }
326 
327  return result;
328 }
329 
330 GeoDataDocument* AlternativeRoutesModel::route( int index )
331 {
332  if ( index >= 0 && index < d->m_routes.size() ) {
333  return d->m_routes.at(index);
334  }
335 
336  return 0;
337 }
338 
339 void AlternativeRoutesModel::newRequest( RouteRequest * )
340 {
341  d->m_responseTime.start();
342  d->m_currentIndex = -1;
343  clear();
344 }
345 
346 void AlternativeRoutesModel::addRestrainedRoutes()
347 {
348  Q_ASSERT( d->m_routes.isEmpty() );
349  qSort( d->m_restrainedRoutes.begin(), d->m_restrainedRoutes.end(), AlternativeRoutesModelPrivate::higherScore );
350 
351  foreach( GeoDataDocument* route, d->m_restrainedRoutes ) {
352  if ( !d->filter( route ) ) {
353  int affected = d->m_routes.size();
354  beginInsertRows( QModelIndex(), affected, affected );
355 // GeoDataDocument* base = d->m_routes.isEmpty() ? 0 : d->m_routes.first();
356  d->m_routes.push_back( route );
357  endInsertRows();
358  }
359  }
360 
361  d->m_restrainedRoutes.clear();
362  Q_ASSERT( !d->m_routes.isEmpty() );
363  setCurrentRoute( 0 );
364 }
365 
366 void AlternativeRoutesModel::addRoute( GeoDataDocument* document, WritePolicy policy )
367 {
368  if ( policy == Instant ) {
369  int affected = d->m_routes.size();
370  beginInsertRows( QModelIndex(), affected, affected );
371  d->m_routes.push_back( document );
372  endInsertRows();
373  return;
374  }
375 
376  if ( d->m_routes.isEmpty() && d->m_restrainedRoutes.isEmpty() ) {
377  // First
378  int responseTime = d->m_responseTime.elapsed();
379  d->m_restrainedRoutes.push_back( document );
380  int timeout = qMin<int>( 500, qMax<int>( 50, responseTime * 2 ) );
381  QTimer::singleShot( timeout, this, SLOT(addRestrainedRoutes()) );
382  return;
383  } else if ( d->m_routes.isEmpty() && !d->m_restrainedRoutes.isEmpty() ) {
384  d->m_restrainedRoutes.push_back( document );
385  } else {
386  for ( int i=0; i<d->m_routes.size(); ++i ) {
387  qreal similarity = AlternativeRoutesModelPrivate::similarity( document, d->m_routes.at( i ) );
388  if ( similarity > 0.8 ) {
389  if ( AlternativeRoutesModelPrivate::higherScore( document, d->m_routes.at( i ) ) ) {
390  d->m_routes[i] = document;
391  QModelIndex changed = index( i );
392  emit dataChanged( changed, changed );
393  }
394 
395  return;
396  }
397  }
398 
399  Q_ASSERT( !d->m_routes.isEmpty() );
400  int affected = d->m_routes.size();
401  beginInsertRows( QModelIndex(), affected, affected );
402  d->m_routes.push_back( document );
403  endInsertRows();
404  }
405 }
406 
407 qreal AlternativeRoutesModel::distance( const GeoDataCoordinates &satellite, const GeoDataCoordinates &lineA, const GeoDataCoordinates &lineB )
408 {
409  return AlternativeRoutesModelPrivate::distance( satellite, lineA, lineB );
410 }
411 
412 QVector<qreal> AlternativeRoutesModel::deviation( const GeoDataDocument* routeA, const GeoDataDocument* routeB )
413 {
414  const GeoDataLineString* waypointsA = waypoints( routeA );
415  const GeoDataLineString* waypointsB = waypoints( routeB );
416  QVector<qreal> result;
417  for ( int a=0; a<waypointsA->size(); ++a ) {
418  result.push_back( AlternativeRoutesModelPrivate::distance( waypointsB, waypointsA->at( a ) ) );
419  }
420  return result;
421 }
422 
423 void AlternativeRoutesModel::update( GeoDataDocument* route )
424 {
425  for ( int i=0; i<d->m_routes.size(); ++i ) {
426  if ( d->m_routes[i] == route ) {
427  emit dataChanged( index( i), index( i ) );
428  }
429  }
430 }
431 
432 const GeoDataLineString* AlternativeRoutesModel::waypoints( const GeoDataDocument* document )
433 {
434  return AlternativeRoutesModelPrivate::waypoints( document );
435 }
436 
437 void AlternativeRoutesModel::setCurrentRoute( int index )
438 {
439  if ( index >= 0 && index < rowCount() && d->m_currentIndex != index ) {
440  d->m_currentIndex = index;
441  emit currentRouteChanged( currentRoute() );
442  emit currentRouteChanged( d->m_currentIndex );
443  }
444 }
445 
446 GeoDataDocument * AlternativeRoutesModel::currentRoute()
447 {
448  GeoDataDocument* result = 0;
449  if ( d->m_currentIndex >= 0 && d->m_currentIndex < rowCount() ) {
450  result = d->m_routes[d->m_currentIndex];
451  }
452 
453  return result;
454 }
455 
456 void AlternativeRoutesModel::clear()
457 {
458  QVector<GeoDataDocument*> routes = d->m_routes;
459  d->m_currentIndex = -1;
460  d->m_routes.clear();
461  beginResetModel();
462  endResetModel();
463  qDeleteAll(routes);
464 }
465 
466 } // namespace Marble
467 
468 #include "AlternativeRoutesModel.moc"
QImage::scanLine
uchar * scanLine(int i)
GeoDataDocument.h
QModelIndex
Marble::GeoDataCoordinates
A 3d point representation.
Definition: GeoDataCoordinates.h:52
RoutingModel.h
Marble::GeoDataDocument
A container for Features, Styles and in the future Schemas.
Definition: GeoDataDocument.h:65
Marble::AlternativeRoutesModel::clear
void clear()
Remove all alternative routes from the model.
Definition: AlternativeRoutesModel.cpp:456
Marble::AlternativeRoutesModel::WritePolicy
WritePolicy
Definition: AlternativeRoutesModel.h:36
Marble::AlternativeRoutesModel::rowCount
int rowCount(const QModelIndex &parent=QModelIndex()) const
Overload of QAbstractListModel.
Definition: AlternativeRoutesModel.cpp:309
Marble::AlternativeRoutesModel::currentRouteChanged
void currentRouteChanged(GeoDataDocument *newRoute)
MarbleMath.h
Marble::AlternativeRoutesModel::addRoute
void addRoute(GeoDataDocument *document, WritePolicy policy=Lazy)
Old data in the model is discarded, the parsed content of the provided document is used as the new mo...
Definition: AlternativeRoutesModel.cpp:366
Marble::AlternativeRoutesModel::newRequest
void newRequest(RouteRequest *request)
Invalidate the current alternative routes and prepare for new ones to arrive.
Definition: AlternativeRoutesModel.cpp:339
Marble::GeoDataLineString::size
int size() const
Returns the number of nodes in a LineString.
Definition: GeoDataLineString.cpp:138
QStringList::contains
bool contains(const QString &str, Qt::CaseSensitivity cs) const
Marble::AlternativeRoutesModel::waypoints
static const GeoDataLineString * waypoints(const GeoDataDocument *document)
Returns the waypoints contained in the route as a linestring.
Definition: AlternativeRoutesModel.cpp:432
Marble::distanceSphere
qreal distanceSphere(qreal lon1, qreal lat1, qreal lon2, qreal lat2)
This method calculates the shortest distance between two points on a sphere.
Definition: MarbleMath.h:52
GeoDataExtendedData.h
Marble::AlternativeRoutesModel::currentRoute
GeoDataDocument * currentRoute()
Definition: AlternativeRoutesModel.cpp:446
QTime
QPointF
Marble::AlternativeRoutesModel::data
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const
Overload of QAbstractListModel.
Definition: AlternativeRoutesModel.cpp:319
QAbstractItemModel::beginResetModel
void beginResetModel()
Marble::RouteRequest
Points to be included in a route.
Definition: RouteRequest.h:31
QObject::name
const char * name() const
Marble::AlternativeRoutesModel::Instant
Definition: AlternativeRoutesModel.h:37
Marble::EARTH_RADIUS
const qreal EARTH_RADIUS
Definition: MarbleGlobal.h:257
QAbstractItemModel::dataChanged
void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
QImage::fill
void fill(uint pixelValue)
QAbstractItemModel::endInsertRows
void endInsertRows()
QObject
QImage::width
int width() const
QAbstractListModel::index
virtual QModelIndex index(int row, int column, const QModelIndex &parent) const
QAbstractListModel
Marble::AlternativeRoutesModel::headerData
QVariant headerData(int section, Qt::Orientation orientation, int role=Qt::DisplayRole) const
Overload of QAbstractListModel.
Definition: AlternativeRoutesModel.cpp:314
QPainter
QModelIndex::row
int row() const
Marble::AlternativeRoutesModel::setCurrentRoute
void setCurrentRoute(int index)
Definition: AlternativeRoutesModel.cpp:437
QColor
GeoDataPlacemark.h
Marble::GeoDataLineString
A LineString that allows to store a contiguous set of line segments.
Definition: GeoDataLineString.h:75
QStringList
Marble::GeoDataLineString::at
GeoDataCoordinates & at(int pos)
Returns a reference to the coordinates of a node at a given position. This method detaches the return...
Definition: GeoDataLineString.cpp:143
QImage
Marble::AlternativeRoutesModel::distance
static qreal distance(const GeoDataCoordinates &satellite, const GeoDataCoordinates &lineA, const GeoDataCoordinates &lineB)
Returns the distance between the given point and the given great circle path.
Definition: AlternativeRoutesModel.cpp:407
QAbstractItemModel::beginInsertRows
void beginInsertRows(const QModelIndex &parent, int first, int last)
GeoDataFolder.h
Marble::AlternativeRoutesModel::deviation
static QVector< qreal > deviation(const GeoDataDocument *routeA, const GeoDataDocument *routeB)
Returns the minimal distance of each waypoint of routeA to routeB.
Definition: AlternativeRoutesModel.cpp:412
Marble::AlternativeRoutesModel::~AlternativeRoutesModel
~AlternativeRoutesModel()
Destructor.
Definition: AlternativeRoutesModel.cpp:304
QVector
QModelIndex::column
int column() const
M_PI
#define M_PI
Definition: GeoDataCoordinates.h:26
AlternativeRoutesModel.h
QVector::push_back
void push_back(const T &value)
QImage::height
int height() const
QtConcurrent::filter
QFuture< void > filter(Sequence &sequence, FilterFunction filterFunction)
Marble::AlternativeRoutesModel::route
GeoDataDocument * route(int index)
Definition: AlternativeRoutesModel.cpp:330
QAbstractItemModel::endResetModel
void endResetModel()
QTimer::singleShot
singleShot
QVariant
QPolygonF
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Mon Jun 22 2020 13:13:38 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