Marble

RoutingLayer.cpp
1// SPDX-License-Identifier: LGPL-2.1-or-later
2//
3// SPDX-FileCopyrightText: 2010 Dennis Nienhüser <nienhueser@kde.org>
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
32namespace Marble
33{
34
35class 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
52public:
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
165RoutingLayerPrivate::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
196int 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
210void 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 );
216 QVariant data = index.data( MarblePlacemarkModel::CoordinateRole );
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
235void 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
256void 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
362void 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
382void 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
396void 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
409QColor RoutingLayerPrivate::alphaAdjusted( const QColor &color, int alpha )
410{
411 QColor result( color );
412 result.setAlpha( alpha );
413 return result;
414}
415
416QPixmap 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
435bool 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 ) ) {
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
511bool 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
545bool 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
592bool 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
620void 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
630void RoutingLayerPrivate::clearStopOver()
631{
632 m_dropStopOver = QPoint();
633 m_dragStopOver = QPoint();
634 emit q->repaintNeeded( m_dirtyRect );
635}
636
637RoutingLayer::RoutingLayer( MarbleWidget *widget, QWidget *parent ) :
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
663{
664 return 1.0;
665}
666
668 const QString& renderPos, GeoSceneLayer *layer )
669{
670 Q_UNUSED( viewport )
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
699RenderState 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
741void 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
751void RoutingLayer::showAlternativeRoutes()
752{
753 setViewportChanged();
754 emit repaintNeeded();
755}
756
757void RoutingLayer::exportRoute()
758{
759 QString fileName = QFileDialog::getSaveFileName( d->m_marbleWidget,
760 tr( "Export Route" ), // krazy:exclude=qclasses
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
777void RoutingLayer::updateRouteState()
778{
779 setViewportChanged();
780 emit repaintNeeded();
781}
782
783void 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
796void RoutingLayer::setInteractive( bool interactive )
797{
798 d->m_isInteractive = interactive;
799}
800
802{
803 return d->m_isInteractive;
804}
805
806QString RoutingLayer::runtimeTrace() const
807{
808 return QStringLiteral("Routing Layer");
809}
810
811} // namespace Marble
812
813#include "moc_RoutingLayer.cpp"
This file contains the headers for MarbleModel.
This file contains the headers for MarbleWidget.
A container for Features, Styles and in the future Schemas.
A class that defines a 3D bounding box for geographic data.
A painter that allows to draw geometric primitives on the map.
Definition GeoPainter.h:89
Layer of a GeoScene document.
This class represents a model of all place marks which are currently available through a given Placem...
A widget class that displays a view of the earth.
MarbleModel * model()
Return the model that this view shows.
void remove(int index)
Remove the element at the given position.
bool isInteractive() const
Returns whether the route is interactive (true by default if not changed by setInteractive)
qreal zValue() const override
Reimplemented from LayerInterface.
~RoutingLayer() override
Destructor.
void setViewContext(ViewContext viewContext)
Set the view context to determine whether the map is used interactively.
bool render(GeoPainter *painter, ViewportParams *viewport, const QString &renderPos="NONE", GeoSceneLayer *layer=nullptr) override
Reimplemented from LayerInterface.
QStringList renderPosition() const override
Reimplemented from LayerInterface.
bool eventFilter(QObject *obj, QEvent *event) override
Overriding QWidget, used to make the layer interactive.
void setInteractive(bool interactive)
Determine whether the route can be edited by the user (via points added, route cleared)
void synchronizeWith(QItemSelectionModel *selection)
Set the proxy model another QAbstractItemView uses that should share its selection model with us.
void setPlacemarkModel(MarblePlacemarkModel *model)
Set the placemark model to use.
AlternativeRoutesModel * alternativeRoutesModel()
Provides access to the model which contains a list of alternative routes.
void retrieveRoute()
Retrieve a route suiting the routeRequest.
void saveRoute(const QString &filename) const
Saves the current route to the file with the given filename.
A public class that controls what is visible in the viewport of a Marble map.
QVariant location(const QVariant &res)
Binds a QML item to a specific geodetic location in screen coordinates.
ViewContext
This enum is used to choose context in which map quality gets used.
@ Still
still image
@ Complete
All data is there and up to date.
@ WaitingForUpdate
Rendering is based on complete, but outdated data, data update was requested.
QString homePath()
MouseButtonPress
QString getSaveFileName(QWidget *parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, Options options)
QPixmap pixmap(QWindow *window, const QSize &size, Mode mode, State state) const const
Qt::KeyboardModifiers modifiers() const const
void clear()
QVariant data(int role) const const
bool isValid() const const
QPoint pos() const const
int x() const const
int y() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
virtual bool event(QEvent *e)
T qobject_cast(QObject *object)
QString tr(const char *sourceText, const char *disambiguation, int n)
void restore()
void save()
int height() const const
int width() const const
bool isNull() const const
int manhattanLength() const const
int x() const const
int y() const const
void adjust(int dx1, int dy1, int dx2, int dy2)
bool contains(const QPoint &p) const const
Qt::MouseButton button() const const
Qt::MouseButtons buttons() const const
bool endsWith(QChar c, Qt::CaseSensitivity cs) const const
bool isEmpty() const const
CaseInsensitive
ArrowCursor
DecorationRole
typedef KeyboardModifiers
RightButton
QTextStream & center(QTextStream &stream)
bool isNull() const const
QString toString() const const
T value() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Jun 21 2024 12:00:07 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.