Marble

RoutingLayer.cpp
1 // SPDX-License-Identifier: LGPL-2.1-or-later
2 //
3 // SPDX-FileCopyrightText: 2010 Dennis Nienhüser <[email protected]>
4 //
5 
6 #include "RoutingLayer.h"
7 
8 #include "GeoDataCoordinates.h"
9 #include "GeoDataLineString.h"
10 #include "GeoPainter.h"
11 #include "MarbleColors.h"
12 #include "MarblePlacemarkModel.h"
13 #include "MarbleWidget.h"
14 #include "MarbleWidgetPopupMenu.h"
15 #include "RoutingModel.h"
16 #include "Route.h"
17 #include "RouteRequest.h"
18 #include "MarbleModel.h"
19 #include "AlternativeRoutesModel.h"
20 #include "RoutingManager.h"
21 #include "Maneuver.h"
22 #include "RenderState.h"
23 
24 #include <QAbstractItemModel>
25 #include <QIcon>
26 #include <QItemSelectionModel>
27 #include <QAction>
28 #include <QMouseEvent>
29 #include <QPixmap>
30 #include <QFileDialog>
31 
32 namespace Marble
33 {
34 
35 class RoutingLayerPrivate
36 {
37  template<class T>
38  struct PaintRegion {
39  T index;
40  QRegion region;
41 
42  PaintRegion( const T &index_, const QRegion &region_ ) :
43  index( index_ ), region( region_ )
44  {
45  // nothing to do
46  }
47  };
48 
49  using ModelRegion = PaintRegion<QModelIndex>;
50  using RequestRegion = PaintRegion<int>;
51 
52 public:
53  RoutingLayer *const q;
54 
55  QList<ModelRegion> m_instructionRegions;
56 
57  QList<RequestRegion> m_regions;
58 
59  QList<RequestRegion> m_alternativeRouteRegions;
60 
61  QList<ModelRegion> m_placemarks;
62 
63  QRegion m_routeRegion;
64 
65  int m_movingIndex;
66 
67  MarbleWidget *const m_marbleWidget;
68 
69  QPixmap m_targetPixmap;
70 
71  QPixmap m_standardRoutePoint;
72 
73  QPixmap m_activeRoutePoint;
74 
75  QRect m_dirtyRect;
76 
77  QPoint m_dropStopOver;
78 
79  QPoint m_dragStopOver;
80 
81  int m_dragStopOverRightIndex;
82 
83  RoutingModel *const m_routingModel;
84 
85  MarblePlacemarkModel *m_placemarkModel;
86 
87  QItemSelectionModel *m_selectionModel;
88 
89  QSize m_pixmapSize;
90 
91  RouteRequest *const m_routeRequest;
92 
93  MarbleWidgetPopupMenu *m_contextMenu;
94 
95  QAction *m_removeViaPointAction;
96 
97  int m_activeMenuIndex;
98 
99  AlternativeRoutesModel *const m_alternativeRoutesModel;
100 
101  ViewContext m_viewContext;
102 
103  bool m_viewportChanged;
104 
105  bool m_isInteractive;
106 
107  /** Constructor */
108  explicit RoutingLayerPrivate( RoutingLayer *parent, MarbleWidget *widget );
109 
110  /** Update the cached drag position. Use an empty point to clear it. */
111  void storeDragPosition( const QPoint &position );
112 
113  // The following methods are mostly only called at one place in the code, but often
114  // Inlined to avoid the function call overhead. Having functions here is just to
115  // keep the code clean
116 
117  /** Returns the same color as the given one with its alpha channel adjusted to the given value */
118  static inline QColor alphaAdjusted( const QColor &color, int alpha );
119 
120  static QPixmap createRoutePoint(const QColor &penColor, const QColor &brushColor);
121 
122  /**
123  * Returns the start or destination position if Ctrl key is among the
124  * provided modifiers, the cached insert position otherwise
125  */
126  inline int viaInsertPosition( Qt::KeyboardModifiers modifiers ) const;
127 
128  /** Paint icons for each placemark in the placemark model */
129  inline void renderPlacemarks( GeoPainter *painter );
130 
131  /** Paint waypoint polygon */
132  inline void renderRoute( GeoPainter *painter );
133 
134  /** Paint turn instruction for selected items */
135  inline void renderAnnotations( GeoPainter *painter ) const;
136 
137  /** Paint alternative routes in gray */
138  inline void renderAlternativeRoutes( GeoPainter *painter );
139 
140  /** Paint icons for trip points etc */
141  inline void renderRequest( GeoPainter *painter );
142 
143  /** Insert via points or emit position signal, if appropriate */
144  inline bool handleMouseButtonRelease( QMouseEvent *e );
145 
146  /** Select route instructions points, start dragging trip points */
147  inline bool handleMouseButtonPress( QMouseEvent *e );
148 
149  /** Dragging trip points, route polygon hovering */
150  inline bool handleMouseMove( QMouseEvent *e );
151 
152  /** True if the given point (screen coordinates) is among the route instruction points */
153  inline bool isInfoPoint( const QPoint &point );
154 
155  /** True if the given point (screen coordinates) is above an alternative route */
156  inline bool isAlternativeRoutePoint( const QPoint &point );
157 
158  /** Paint the stopover indicator pixmap at the given position. Also repaints the old position */
159  inline void paintStopOver( QRect position );
160 
161  /** Removes the stopover indicator pixmap. Also repaints its old position */
162  inline void clearStopOver();
163 };
164 
165 RoutingLayerPrivate::RoutingLayerPrivate( RoutingLayer *parent, MarbleWidget *widget ) :
166  q( parent ), m_movingIndex( -1 ), m_marbleWidget( widget ),
167  m_targetPixmap(QStringLiteral(":/data/bitmaps/routing_pick.png")),
168  m_standardRoutePoint(createRoutePoint(widget->model()->routingManager()->routeColorStandard(),
169  widget->model()->routingManager()->routeColorAlternative())),
170  m_activeRoutePoint(createRoutePoint(widget->model()->routingManager()->routeColorHighlighted(),
171  alphaAdjusted(Oxygen::hotOrange4, 200))),
172  m_dragStopOverRightIndex(-1),
173  m_routingModel( widget->model()->routingManager()->routingModel() ),
174  m_placemarkModel( nullptr ),
175  m_selectionModel( nullptr ),
176  m_pixmapSize( 22, 22 ),
177  m_routeRequest( widget->model()->routingManager()->routeRequest() ),
178  m_activeMenuIndex( -1 ),
179  m_alternativeRoutesModel( widget->model()->routingManager()->alternativeRoutesModel() ),
180  m_viewContext( Still ),
181  m_viewportChanged( true ),
182  m_isInteractive( true )
183 {
184  m_contextMenu = new MarbleWidgetPopupMenu( m_marbleWidget, m_marbleWidget->model() );
185  m_removeViaPointAction = new QAction( QObject::tr( "&Remove this Destination" ), q );
186  QObject::connect( m_removeViaPointAction, SIGNAL(triggered()), q, SLOT(removeViaPoint()) );
187  m_contextMenu->addAction( Qt::RightButton, m_removeViaPointAction );
188  QAction *exportAction = new QAction( QObject::tr( "&Export Route..." ), q );
189  QObject::connect( exportAction, SIGNAL(triggered()), q, SLOT(exportRoute()) );
190  m_contextMenu->addAction( Qt::RightButton, exportAction );
191  if ( MarbleGlobal::getInstance()->profiles() & MarbleGlobal::SmallScreen ) {
192  m_pixmapSize = QSize( 38, 38 );
193  }
194 }
195 
196 int RoutingLayerPrivate::viaInsertPosition( Qt::KeyboardModifiers modifiers ) const
197 {
198  if ( modifiers & Qt::ControlModifier ) {
199  bool leftHand = m_routeRequest->size() / 2 >= m_dragStopOverRightIndex;
200  if ( leftHand && m_routeRequest->size() > 2 ) {
201  return 0;
202  } else {
203  return m_routeRequest->size();
204  }
205  } else {
206  return m_dragStopOverRightIndex;
207  }
208 }
209 
210 void RoutingLayerPrivate::renderPlacemarks( GeoPainter *painter )
211 {
212  m_placemarks.clear();
213  painter->setPen( QColor( Qt::black ) );
214  for ( int i = 0; i < m_placemarkModel->rowCount(); ++i ) {
215  QModelIndex index = m_placemarkModel->index( i, 0 );
217  if ( index.isValid() && !data.isNull() ) {
218  GeoDataCoordinates pos = data.value<GeoDataCoordinates>();
219 
220  QPixmap pixmap = index.data( Qt::DecorationRole ).value<QPixmap>();
221  if ( !pixmap.isNull() && m_selectionModel->isSelected( index ) ) {
222  QIcon selected = QIcon( pixmap );
223  QPixmap result = selected.pixmap( m_pixmapSize, QIcon::Selected, QIcon::On );
224  painter->drawPixmap( pos, result );
225  } else {
226  painter->drawPixmap( pos, pixmap );
227  }
228 
229  const QRegion region = painter->regionFromPixmapRect(pos, m_targetPixmap.width(), m_targetPixmap.height());
230  m_placemarks.push_back( ModelRegion( index, region ) );
231  }
232  }
233 }
234 
235 void RoutingLayerPrivate::renderAlternativeRoutes( GeoPainter *painter )
236 {
237  QPen alternativeRoutePen( m_marbleWidget->model()->routingManager()->routeColorAlternative() );
238  alternativeRoutePen.setWidth( 5 );
239  painter->setPen( alternativeRoutePen );
240 
241  for ( int i=0; i<m_alternativeRoutesModel->rowCount(); ++i ) {
242  const GeoDataDocument *route = m_alternativeRoutesModel->route(i);
243  if ( route && route != m_alternativeRoutesModel->currentRoute() ) {
244  const GeoDataLineString* points = AlternativeRoutesModel::waypoints( route );
245  if ( points ) {
246  painter->drawPolyline( *points );
247  if ( m_viewportChanged && m_isInteractive && m_viewContext == Still ) {
248  QRegion region = painter->regionFromPolyline( *points, 8 );
249  m_alternativeRouteRegions.push_back( RequestRegion( i, region ) );
250  }
251  }
252  }
253  }
254 }
255 
256 void RoutingLayerPrivate::renderRoute( GeoPainter *painter )
257 {
258  GeoDataLineString waypoints = m_routingModel->route().path();
259 
260  QPen standardRoutePen( m_marbleWidget->model()->routingManager()->routeColorStandard() );
261  standardRoutePen.setWidth( 5 );
262  if ( m_marbleWidget->model()->routingManager()->state() == RoutingManager::Downloading ) {
263  standardRoutePen.setStyle( Qt::DotLine );
264  }
265  painter->setPen( standardRoutePen );
266 
267  painter->drawPolyline( waypoints );
268  if ( m_viewportChanged && m_viewContext == Still ) {
269  int const offset = MarbleGlobal::getInstance()->profiles() & MarbleGlobal::SmallScreen ? 24 : 8;
270  if ( m_isInteractive ) {
271  m_routeRegion = painter->regionFromPolyline( waypoints, offset );
272  }
273  }
274 
275 
276  standardRoutePen.setWidth( 2 );
277  painter->setPen( standardRoutePen );
278 
279  // Map matched position
280  //painter->setBrush( QBrush( Oxygen::brickRed4) );
281  //painter->drawEllipse( m_routingModel->route().positionOnRoute(), 8, 8 );
282 
283  painter->setBrush( QBrush( m_marbleWidget->model()->routingManager()->routeColorAlternative() ) );
284 
285  if ( !m_dropStopOver.isNull() ) {
286  int dx = 1 + m_pixmapSize.width() / 2;
287  int dy = 1 + m_pixmapSize.height() / 2;
288  QPoint center = m_dropStopOver - QPoint( dx, dy );
289  painter->drawPixmap( center, m_targetPixmap );
290 
291  if ( !m_dragStopOver.isNull() && m_dragStopOverRightIndex >= 0 && m_dragStopOverRightIndex <= m_routeRequest->size() ) {
292  QPoint moved = m_dropStopOver - m_dragStopOver;
293  if ( moved.manhattanLength() > 10 ) {
294  qreal lon( 0.0 ), lat( 0.0 );
295  if ( m_marbleWidget->geoCoordinates( m_dropStopOver.x(), m_dropStopOver.y(),
296  lon, lat, GeoDataCoordinates::Radian ) ) {
297  GeoDataCoordinates drag( lon, lat );
298  standardRoutePen.setStyle( Qt::DotLine );
299  painter->setPen( standardRoutePen );
300  GeoDataLineString lineString;
301  if ( m_dragStopOverRightIndex > 0 ) {
302  lineString << m_routeRequest->at( m_dragStopOverRightIndex-1 );
303  }
304  lineString << drag;
305  if ( m_dragStopOverRightIndex < m_routeRequest->size() ) {
306  lineString << m_routeRequest->at( m_dragStopOverRightIndex );
307  }
308  painter->drawPolyline( lineString );
309  standardRoutePen.setStyle( Qt::SolidLine );
310  painter->setPen( standardRoutePen );
311  }
312  }
313  }
314  }
315 
316  if ( m_viewContext == Animation ) {
317  return;
318  }
319 
320  if( m_routingModel->rowCount() == m_routingModel->route().size() ) {
321  m_instructionRegions.clear();
322 
323  QPen activeRouteSegmentPen(m_marbleWidget->model()->routingManager()->routeColorHighlighted());
324  activeRouteSegmentPen.setWidth(6);
325  if (m_marbleWidget->model()->routingManager()->state() == RoutingManager::Downloading) {
326  activeRouteSegmentPen.setStyle(Qt::DotLine);
327  }
328  painter->setPen(activeRouteSegmentPen);
329 
330  for ( int i = 0; i < m_routingModel->rowCount(); ++i ) {
331  QModelIndex index = m_routingModel->index( i, 0 );
332  GeoDataCoordinates pos = index.data( MarblePlacemarkModel::CoordinateRole ).value<GeoDataCoordinates>();
333 
334  if ( m_selectionModel && m_selectionModel->selection().contains( index ) ) {
335  const RouteSegment &segment = m_routingModel->route().at( i );
336  const GeoDataLineString currentRoutePoints = segment.path();
337 
338  painter->drawPolyline( currentRoutePoints );
339 
340  painter->drawPixmap(pos, m_activeRoutePoint);
341  }
342  else {
343  painter->drawPixmap(pos, m_standardRoutePoint);
344  }
345 
346  if ( m_isInteractive ) {
347  QRegion region = painter->regionFromEllipse( pos, 12, 12 );
348  m_instructionRegions.push_front( ModelRegion( index, region ) );
349  }
350  }
351  }
352 
353  if( !m_routingModel->deviatedFromRoute() ) {
354  GeoDataCoordinates location = m_routingModel->route().currentSegment().nextRouteSegment().maneuver().position();
355  QString nextInstruction = m_routingModel->route().currentSegment().nextRouteSegment().maneuver().instructionText();
356  if( !nextInstruction.isEmpty() ) {
357  painter->drawPixmap(location, m_activeRoutePoint);
358  }
359  }
360 }
361 
362 void RoutingLayerPrivate::renderAnnotations( GeoPainter *painter ) const
363 {
364  if ( !m_selectionModel || m_selectionModel->selection().isEmpty() ) {
365  // nothing to do
366  return;
367  }
368 
369  for ( int i = 0; i < m_routingModel->rowCount(); ++i ) {
370  QModelIndex index = m_routingModel->index( i, 0 );
371 
372  if ( m_selectionModel->selection().contains( index ) ) {
373  bool const smallScreen = MarbleGlobal::getInstance()->profiles() & MarbleGlobal::SmallScreen;
374  GeoDataCoordinates pos = index.data( MarblePlacemarkModel::CoordinateRole ).value<GeoDataCoordinates>();
375  painter->setPen( QColor( Qt::black ) );
376  painter->setBrush( QBrush( Oxygen::sunYellow6 ) );
377  painter->drawAnnotation( pos, index.data().toString(), QSize( smallScreen ? 240 : 120, 0 ), 10, 30, 5, 5 );
378  }
379  }
380 }
381 
382 void RoutingLayerPrivate::renderRequest( GeoPainter *painter )
383 {
384  m_regions.clear();
385  for ( int i = 0; i < m_routeRequest->size(); ++i ) {
386  const GeoDataCoordinates pos = m_routeRequest->at( i );
387  if ( pos.isValid() ) {
388  QPixmap pixmap = m_routeRequest->pixmap( i );
389  painter->drawPixmap( pos, pixmap );
390  const QRegion region = painter->regionFromPixmapRect(pos, pixmap.width(), pixmap.height());
391  m_regions.push_front( RequestRegion( i, region ) );
392  }
393  }
394 }
395 
396 void RoutingLayerPrivate::storeDragPosition( const QPoint &pos )
397 {
398  m_dragStopOver = pos;
399  m_dragStopOverRightIndex = -1;
400 
401  qreal lon( 0.0 ), lat( 0.0 );
402  if ( m_routeRequest && !pos.isNull()
403  && m_marbleWidget->geoCoordinates( pos.x(), pos.y(), lon, lat, GeoDataCoordinates::Radian ) ) {
404  GeoDataCoordinates waypoint( lon, lat );
405  m_dragStopOverRightIndex = m_routingModel->rightNeighbor( waypoint, m_routeRequest );
406  }
407 }
408 
409 QColor RoutingLayerPrivate::alphaAdjusted( const QColor &color, int alpha )
410 {
411  QColor result( color );
412  result.setAlpha( alpha );
413  return result;
414 }
415 
416 QPixmap RoutingLayerPrivate::createRoutePoint(const QColor &penColor, const QColor &brushColor)
417 {
418  QPen pen(penColor);
419  pen.setWidth(2);
420 
421  const QBrush brush(brushColor);
422 
423  QPixmap routePoint(QSize(8, 8));
424  routePoint.fill(Qt::transparent);
425 
426  QPainter painter(&routePoint);
427  painter.setRenderHint(QPainter::Antialiasing, true);
428  painter.setPen(pen);
429  painter.setBrush(brush);
430  painter.drawEllipse(1, 1, 6, 6);
431 
432  return routePoint;
433 }
434 
435 bool RoutingLayerPrivate::handleMouseButtonPress( QMouseEvent *e )
436 {
437  for( const RequestRegion &region: m_regions ) {
438  if ( region.region.contains( e->pos() ) ) {
439  if ( e->button() == Qt::LeftButton ) {
440  m_movingIndex = region.index;
441  m_dropStopOver = QPoint();
442  m_dragStopOver = QPoint();
443  return true;
444  } else if ( e->button() == Qt::RightButton ) {
445  m_removeViaPointAction->setEnabled( true );
446  m_activeMenuIndex = region.index;
447  m_contextMenu->showRmbMenu( e->x(), e->y() );
448  return true;
449  } else
450  return false;
451  }
452  }
453 
454  for( const ModelRegion &region: m_instructionRegions ) {
455  if ( region.region.contains( e->pos() ) && m_selectionModel ) {
456  if ( e->button() == Qt::LeftButton ) {
458  if ( m_selectionModel->isSelected( region.index ) ) {
459  command = QItemSelectionModel::Clear;
460  }
461  m_selectionModel->select( region.index, command );
462  m_dropStopOver = e->pos();
463  storeDragPosition( e->pos() );
464  // annotation and old annotation are dirty, large region
465  emit q->repaintNeeded();
466  return true;
467  } else if ( e->button() == Qt::RightButton ) {
468  m_removeViaPointAction->setEnabled( false );
469  m_contextMenu->showRmbMenu( e->x(), e->y() );
470  return true;
471  } else
472  return false;
473  }
474  }
475 
476  if ( m_routeRegion.contains( e->pos() ) ) {
477  if ( e->button() == Qt::LeftButton ) {
478  /** @todo: Determine the neighbored via points and insert in order */
479  m_dropStopOver = e->pos();
480  storeDragPosition( e->pos() );
481  return true;
482  } else if ( e->button() == Qt::RightButton ) {
483  m_removeViaPointAction->setEnabled( false );
484  m_contextMenu->showRmbMenu( e->x(), e->y() );
485  return true;
486  } else
487  return false;
488  }
489 
490  if ( e->button() != Qt::LeftButton ) {
491  return false;
492  }
493 
494  for( const RequestRegion &region: m_alternativeRouteRegions ) {
495  if ( region.region.contains( e->pos() ) ) {
496  m_alternativeRoutesModel->setCurrentRoute( region.index );
497  return true;
498  }
499  }
500 
501  for( const ModelRegion &region: m_placemarks ) {
502  if ( region.region.contains( e->pos() ) ) {
503  emit q->placemarkSelected( region.index );
504  return true;
505  }
506  }
507 
508  return false;
509 }
510 
511 bool RoutingLayerPrivate::handleMouseButtonRelease( QMouseEvent *e )
512 {
513  if ( e->button() != Qt::LeftButton ) {
514  return false;
515  }
516 
517  if ( m_movingIndex >= 0 ) {
518  m_movingIndex = -1;
519  clearStopOver();
520  m_marbleWidget->model()->routingManager()->retrieveRoute();
521  return true;
522  }
523 
524  if ( !m_dropStopOver.isNull() && !m_dragStopOver.isNull() ) {
525  QPoint moved = e->pos() - m_dragStopOver;
526  if ( moved.manhattanLength() < 10 ) {
527  return false;
528  }
529 
530  qreal lon( 0.0 ), lat( 0.0 );
531  if ( m_dragStopOverRightIndex >= 0 && m_dragStopOverRightIndex <= m_routeRequest->size()
532  && m_marbleWidget->geoCoordinates( m_dropStopOver.x(), m_dropStopOver.y(), lon, lat, GeoDataCoordinates::Radian ) ) {
533  GeoDataCoordinates position( lon, lat );
534  m_dragStopOverRightIndex = viaInsertPosition( e->modifiers() );
535  m_routeRequest->insert( m_dragStopOverRightIndex, position );
536  clearStopOver();
537  m_marbleWidget->model()->routingManager()->retrieveRoute();
538  return true;
539  }
540  }
541 
542  return false;
543 }
544 
545 bool RoutingLayerPrivate::handleMouseMove( QMouseEvent *e )
546 {
547  qreal lon( 0.0 ), lat( 0.0 );
548  if ( m_marbleWidget->geoCoordinates( e->pos().x(), e->pos().y(),
549  lon, lat, GeoDataCoordinates::Radian ) ) {
550 
551  if ( m_movingIndex >= 0 ) {
552  GeoDataCoordinates moved( lon, lat );
553  m_routeRequest->setPosition( m_movingIndex, moved );
554  m_marbleWidget->setCursor( Qt::ArrowCursor );
555  } else if ( !m_dragStopOver.isNull() ) {
556  // Repaint only that region of the map that is affected by the change
557  m_dragStopOverRightIndex = viaInsertPosition( e->modifiers() );
558  QRect dirty = m_routeRegion.boundingRect();
559  dirty |= QRect( m_dropStopOver, m_pixmapSize );
560  dirty |= QRect( e->pos(), m_pixmapSize );
561  if ( e->buttons() & Qt::LeftButton ) {
562  m_dropStopOver = e->pos();
563  } else {
564  m_dragStopOver = QPoint();
565  m_dropStopOver = QPoint();
566  }
567  emit q->repaintNeeded( dirty );
568  m_marbleWidget->setCursor( Qt::ArrowCursor );
569  } else if ( isInfoPoint( e->pos() ) ) {
570  clearStopOver();
571  m_marbleWidget->setCursor( Qt::ArrowCursor );
572  } else if ( m_routeRegion.contains( e->pos() ) ) {
573  m_dropStopOver = e->pos();
574  m_marbleWidget->setCursor( Qt::ArrowCursor );
575  } else if ( !m_dropStopOver.isNull() ) {
576  clearStopOver();
577  } else if ( isAlternativeRoutePoint( e->pos() ) ) {
578  m_marbleWidget->setCursor( Qt::ArrowCursor );
579  }
580  else {
581  return false;
582  }
583 
584  // Update pixmap in the map (old and new position needs repaint)
585  paintStopOver( QRect( e->pos(), m_pixmapSize ) );
586  return true;
587  }
588 
589  return false;
590 }
591 
592 bool RoutingLayerPrivate::isInfoPoint( const QPoint &point )
593 {
594  for( const RequestRegion &region: m_regions ) {
595  if ( region.region.contains( point ) ) {
596  return true;
597  }
598  }
599 
600  for( const ModelRegion &region: m_instructionRegions ) {
601  if ( region.region.contains( point ) ) {
602  return true;
603  }
604  }
605 
606  return false;
607 }
608 
609  bool RoutingLayerPrivate::isAlternativeRoutePoint( const QPoint &point )
610  {
611  for( const RequestRegion &region: m_alternativeRouteRegions ) {
612  if ( region.region.contains( point ) ) {
613  return true;
614  }
615  }
616 
617  return false;
618  }
619 
620 void RoutingLayerPrivate::paintStopOver( QRect dirty )
621 {
622  emit q->repaintNeeded( m_dirtyRect );
623  int dx = 1 + m_pixmapSize.width() / 2;
624  int dy = 1 + m_pixmapSize.height() / 2;
625  dirty.adjust( -dx, -dy, -dx, -dy );
626  emit q->repaintNeeded( dirty );
627  m_dirtyRect = dirty;
628 }
629 
630 void RoutingLayerPrivate::clearStopOver()
631 {
632  m_dropStopOver = QPoint();
633  m_dragStopOver = QPoint();
634  emit q->repaintNeeded( m_dirtyRect );
635 }
636 
638  QObject( parent ), d( new RoutingLayerPrivate( this, widget ) )
639 {
640  connect( widget->model()->routingManager(), SIGNAL(stateChanged(RoutingManager::State)),
641  this, SLOT(updateRouteState()) );
642  connect( widget, SIGNAL(visibleLatLonAltBoxChanged(GeoDataLatLonAltBox)),
643  this, SLOT(setViewportChanged()) );
644  connect(widget->model()->routingManager()->alternativeRoutesModel(), SIGNAL(currentRouteChanged(const GeoDataDocument*)),
645  this, SLOT(setViewportChanged()) );
646  connect(widget->model()->routingManager()->alternativeRoutesModel(), SIGNAL(currentRouteChanged(const GeoDataDocument*)),
647  this, SIGNAL(repaintNeeded()) );
648  connect( widget->model()->routingManager()->alternativeRoutesModel(), SIGNAL(rowsInserted(QModelIndex,int,int)),
649  this, SLOT(showAlternativeRoutes()) );
650 }
651 
653 {
654  delete d;
655 }
656 
658 {
659  return QStringList(QStringLiteral("HOVERS_ABOVE_SURFACE"));
660 }
661 
662 qreal RoutingLayer::zValue() const
663 {
664  return 1.0;
665 }
666 
668  const QString& renderPos, GeoSceneLayer *layer )
669 {
670  Q_UNUSED( viewport )
671  Q_UNUSED( renderPos )
672  Q_UNUSED( layer )
673 
674  painter->save();
675 
676  if ( d->m_placemarkModel) {
677  d->renderPlacemarks( painter );
678  }
679 
680  if ( d->m_alternativeRoutesModel ) {
681  d->renderAlternativeRoutes( painter );
682  }
683 
684  d->renderRoute( painter );
685 
686  if ( d->m_routeRequest) {
687  d->renderRequest( painter );
688  }
689 
690  d->renderAnnotations( painter );
691 
692  painter->restore();
693  if ( d->m_viewportChanged && d->m_viewContext == Still ) {
694  d->m_viewportChanged = false;
695  }
696  return true;
697 }
698 
699 RenderState RoutingLayer::renderState() const
700 {
701  return RenderState(QStringLiteral("Routing"), d->m_marbleWidget->model()->routingManager()->state() == RoutingManager::Downloading ? WaitingForUpdate : Complete);
702 }
703 
705 {
706  Q_UNUSED( obj )
707 
708  if ( !d->m_isInteractive ) {
709  return false;
710  }
711 
712  if ( event->type() == QEvent::MouseButtonPress ) {
713  QMouseEvent *e = static_cast<QMouseEvent*>( event );
714  return d->handleMouseButtonPress( e );
715  }
716 
717  if ( event->type() == QEvent::MouseButtonRelease ) {
718  QMouseEvent *e = static_cast<QMouseEvent*>( event );
719  return d->handleMouseButtonRelease( e );
720  }
721 
722  if ( event->type() == QEvent::MouseMove ) {
723  QMouseEvent *e = static_cast<QMouseEvent*>( event );
724  return d->handleMouseMove( e );
725  }
726 
727  return false;
728 }
729 
731 {
732  d->m_placemarkModel = model;
733  setViewportChanged();
734 }
735 
737 {
738  d->m_selectionModel = selection;
739 }
740 
741 void RoutingLayer::removeViaPoint()
742 {
743  if ( d->m_activeMenuIndex >= 0 ) {
744  d->m_routeRequest->remove( d->m_activeMenuIndex );
745  d->m_activeMenuIndex = -1;
746  emit repaintNeeded();
747  d->m_marbleWidget->model()->routingManager()->retrieveRoute();
748  }
749 }
750 
751 void RoutingLayer::showAlternativeRoutes()
752 {
753  setViewportChanged();
754  emit repaintNeeded();
755 }
756 
757 void RoutingLayer::exportRoute()
758 {
759  QString fileName = QFileDialog::getSaveFileName( d->m_marbleWidget,
760  tr( "Export Route" ), // krazy:exclude=qclasses
761  QDir::homePath(),
762  tr( "GPX and KML files (*.gpx *.kml)" ) );
763 
764  if ( !fileName.isEmpty() ) {
765  if ( fileName.endsWith( QLatin1String( ".gpx" ), Qt::CaseInsensitive ) ) {
766  QFile gpx( fileName );
767  if ( gpx.open( QFile::WriteOnly) ) {
768  d->m_routingModel->exportGpx( &gpx );
769  gpx.close();
770  }
771  } else {
772  d->m_marbleWidget->model()->routingManager()->saveRoute( fileName );
773  }
774  }
775 }
776 
777 void RoutingLayer::updateRouteState()
778 {
779  setViewportChanged();
780  emit repaintNeeded();
781 }
782 
783 void RoutingLayer::setViewportChanged()
784 {
785  d->m_viewportChanged = true;
786  d->m_routeRegion = QRegion();
787  d->m_instructionRegions.clear();
788  d->m_alternativeRouteRegions.clear();
789 }
790 
792 {
793  d->m_viewContext = viewContext;
794 }
795 
796 void RoutingLayer::setInteractive( bool interactive )
797 {
798  d->m_isInteractive = interactive;
799 }
800 
802 {
803  return d->m_isInteractive;
804 }
805 
806 QString RoutingLayer::runtimeTrace() const
807 {
808  return QStringLiteral("Routing Layer");
809 }
810 
811 } // namespace Marble
812 
813 #include "moc_RoutingLayer.cpp"
ViewContext
This enum is used to choose context in which map quality gets used.
Definition: MarbleGlobal.h:66
bool isNull() const const
QPoint pos() const const
bool endsWith(const QString &s, Qt::CaseSensitivity cs) const const
bool isNull() const const
A class that defines a 3D bounding box for geographic data.
DecorationRole
MouseButtonPress
QVariant location(const QVariant &res)
CaseInsensitive
bool render(GeoPainter *painter, ViewportParams *viewport, const QString &renderPos="NONE", GeoSceneLayer *layer=nullptr) override
Reimplemented from LayerInterface.
Qt::MouseButton button() const const
T value() const const
MarbleModel * model()
Return the model that this view shows.
@ Animation
animated view (e.g. while rotating the globe)
Definition: MarbleGlobal.h:68
void adjust(int dx1, int dy1, int dx2, int dy2)
A widget class that displays a view of the earth.
Definition: MarbleWidget.h:98
QStringList renderPosition() const override
Reimplemented from LayerInterface.
int x() const const
int y() const const
QString homePath()
@ Still
still image
Definition: MarbleGlobal.h:67
RightButton
Layer of a GeoScene document.
Definition: GeoSceneLayer.h:28
void setPlacemarkModel(MarblePlacemarkModel *model)
Set the placemark model to use.
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QVariant data(int role) const const
virtual bool event(QEvent *e)
AlternativeRoutesModel * alternativeRoutesModel()
Provides access to the model which contains a list of alternative routes.
A public class that controls what is visible in the viewport of a Marble map.
bool isEmpty() const const
Qt::MouseButtons buttons() const const
ArrowCursor
void synchronizeWith(QItemSelectionModel *selection)
Set the proxy model another QAbstractItemView uses that should share its selection model with us.
A container for Features, Styles and in the future Schemas.
int x() const const
int y() const const
void setInteractive(bool interactive)
Determine whether the route can be edited by the user (via points added, route cleared)
int manhattanLength() const const
@ CoordinateRole
The GeoDataCoordinates coordinate.
Binds a QML item to a specific geodetic location in screen coordinates.
int height() const const
@ WaitingForUpdate
Rendering is based on complete, but outdated data, data update was requested.
Definition: MarbleGlobal.h:177
bool isValid() const const
bool eventFilter(QObject *obj, QEvent *event) override
Overriding QWidget, used to make the layer interactive.
A painter that allows to draw geometric primitives on the map.
Definition: GeoPainter.h:88
Qt::KeyboardModifiers modifiers() const const
@ Complete
All data is there and up to date.
Definition: MarbleGlobal.h:176
QString getSaveFileName(QWidget *parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options)
QPixmap pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state) const const
void setViewContext(ViewContext viewContext)
Set the view context to determine whether the map is used interactively.
RoutingLayer(MarbleWidget *widget, QWidget *parent=nullptr)
Constructor.
void restore()
qreal zValue() const override
Reimplemented from LayerInterface.
~RoutingLayer() override
Destructor.
QTextStream & center(QTextStream &stream)
void save()
This class represents a model of all place marks which are currently available through a given Placem...
bool contains(const QPoint &p) const const
typedef KeyboardModifiers
QString tr(const char *sourceText, const char *disambiguation, int n)
int width() const const
bool isInteractive() const
Returns whether the route is interactive (true by default if not changed by setInteractive)
QString toString() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2022 The KDE developers.
Generated on Thu May 26 2022 04:07:50 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.