29 #include <QAbstractItemModel>
31 #include <QItemSelectionModel>
34 #include <QMouseEvent>
36 #include <QFileDialog>
41 class RoutingLayerPrivate
48 PaintRegion(
const T &index_,
const QRegion ®ion_ ) :
49 index( index_ ), region( region_ )
55 typedef PaintRegion<QModelIndex> ModelRegion;
56 typedef PaintRegion<int> RequestRegion;
59 RoutingLayer *
const q;
61 QList<ModelRegion> m_instructionRegions;
63 QList<RequestRegion> m_regions;
65 QList<RequestRegion> m_alternativeRouteRegions;
67 QList<ModelRegion> m_placemarks;
69 QRegion m_routeRegion;
75 QPixmap m_targetPixmap;
79 QPoint m_dropStopOver;
81 QPoint m_dragStopOver;
83 int m_dragStopOverRightIndex;
85 RoutingModel *
const m_routingModel;
87 MarblePlacemarkModel *m_placemarkModel;
89 QItemSelectionModel *m_selectionModel;
95 RouteRequest *
const m_routeRequest;
97 MarbleWidgetPopupMenu *m_contextMenu;
99 QAction *m_removeViaPointAction;
101 int m_activeMenuIndex;
103 AlternativeRoutesModel *
const m_alternativeRoutesModel;
107 bool m_viewportChanged;
110 explicit RoutingLayerPrivate( RoutingLayer *parent,
MarbleWidget *widget );
113 void storeDragPosition(
const QPoint &position );
120 inline QColor alphaAdjusted(
const QColor &color,
int alpha )
const;
126 inline int viaInsertPosition( Qt::KeyboardModifiers modifiers )
const;
129 inline void renderPlacemarks( GeoPainter *painter );
132 inline void renderRoute( GeoPainter *painter );
135 inline void renderAnnotations( GeoPainter *painter );
138 inline void renderAlternativeRoutes( GeoPainter *painter );
141 inline void renderRequest( GeoPainter *painter );
144 void setRouteDirty(
bool dirty );
147 inline bool handleMouseButtonRelease( QMouseEvent *e );
150 inline bool handleMouseButtonPress( QMouseEvent *e );
153 inline bool handleMouseMove( QMouseEvent *e );
156 inline bool isInfoPoint(
const QPoint &point );
159 inline bool isAlternativeRoutePoint(
const QPoint &point );
162 inline void paintStopOver( QRect position );
165 inline void clearStopOver();
168 RoutingLayerPrivate::RoutingLayerPrivate( RoutingLayer *parent,
MarbleWidget *widget ) :
169 q( parent ), m_movingIndex( -1 ), m_marbleWidget( widget ),
170 m_targetPixmap(
":/data/bitmaps/routing_pick.png" ), m_dragStopOverRightIndex( -1 ),
171 m_routingModel( widget->model()->routingManager()->routingModel() ),
172 m_placemarkModel( 0 ), m_selectionModel( 0 ), m_routeDirty( false ), m_pixmapSize( 22, 22 ),
173 m_routeRequest( widget->model()->routingManager()->routeRequest() ),
174 m_activeMenuIndex( -1 ),
175 m_alternativeRoutesModel( widget->model()->routingManager()->alternativeRoutesModel() ),
176 m_viewContext(
Still ), m_viewportChanged( true )
178 m_contextMenu =
new MarbleWidgetPopupMenu( m_marbleWidget, m_marbleWidget->model() );
179 m_removeViaPointAction =
new QAction( QObject::tr(
"&Remove this destination" ), q );
180 QObject::connect( m_removeViaPointAction, SIGNAL(triggered()), q, SLOT(removeViaPoint()) );
181 m_contextMenu->addAction( Qt::RightButton, m_removeViaPointAction );
182 QAction *exportAction =
new QAction( QObject::tr(
"&Export route..." ), q );
183 QObject::connect( exportAction, SIGNAL(triggered()), q, SLOT(exportRoute()) );
184 m_contextMenu->addAction( Qt::RightButton, exportAction );
186 m_pixmapSize = QSize( 38, 38 );
191 int RoutingLayerPrivate::viaInsertPosition( Qt::KeyboardModifiers modifiers )
const
193 if ( modifiers & Qt::ControlModifier ) {
194 bool leftHand = m_routeRequest->size() / 2 >= m_dragStopOverRightIndex;
195 if ( leftHand && m_routeRequest->size() > 2 ) {
198 return m_routeRequest->size();
201 return m_dragStopOverRightIndex;
205 void RoutingLayerPrivate::renderPlacemarks( GeoPainter *painter )
207 m_placemarks.clear();
208 painter->setPen( QColor( Qt::black ) );
209 for (
int i = 0; i < m_placemarkModel->rowCount(); ++i ) {
210 QModelIndex index = m_placemarkModel->index( i, 0 );
211 QVariant data = index.data( MarblePlacemarkModel::CoordinateRole );
212 if ( index.isValid() && !data.isNull() ) {
213 GeoDataCoordinates pos = data.value<GeoDataCoordinates>();
215 QPixmap pixmap = index.data( Qt::DecorationRole ).value<QPixmap>();
216 if ( !pixmap.isNull() && m_selectionModel->isSelected( index ) ) {
217 QIcon selected = QIcon( pixmap );
218 QPixmap result = selected.pixmap( m_pixmapSize, QIcon::Selected, QIcon::On );
219 painter->drawPixmap( pos, result );
221 painter->drawPixmap( pos, pixmap );
224 QRegion region = painter->regionFromRect( pos, m_targetPixmap.width(), m_targetPixmap.height() );
225 m_placemarks.push_back( ModelRegion( index, region ) );
230 void RoutingLayerPrivate::renderAlternativeRoutes( GeoPainter *painter )
232 QPen alternativeRoutePen( m_marbleWidget->model()->routingManager()->routeColorAlternative() );
233 alternativeRoutePen.setWidth( 5 );
234 painter->setPen( alternativeRoutePen );
236 for (
int i=0; i<m_alternativeRoutesModel->rowCount(); ++i ) {
237 GeoDataDocument* route = m_alternativeRoutesModel->route( i );
238 if ( route && route != m_alternativeRoutesModel->currentRoute() ) {
239 GeoDataLineString* points = AlternativeRoutesModel::waypoints( route );
241 painter->drawPolyline( *points );
242 if ( m_viewportChanged && m_viewContext ==
Still ) {
243 QRegion region = painter->regionFromPolyline( *points, 8 );
244 m_alternativeRouteRegions.push_back( RequestRegion( i, region ) );
251 void RoutingLayerPrivate::renderRoute( GeoPainter *painter )
253 GeoDataLineString waypoints = m_routingModel->route().path();
255 QPen standardRoutePen( m_marbleWidget->model()->routingManager()->routeColorStandard() );
256 standardRoutePen.setWidth( 5 );
257 if ( m_routeDirty ) {
258 standardRoutePen.setStyle( Qt::DotLine );
260 painter->setPen( standardRoutePen );
262 painter->drawPolyline( waypoints );
263 if ( m_viewportChanged && m_viewContext ==
Still ) {
264 int const offset = MarbleGlobal::getInstance()->profiles() & MarbleGlobal::SmallScreen ? 24 : 8;
265 m_routeRegion = painter->regionFromPolyline( waypoints, offset );
269 standardRoutePen.setWidth( 2 );
270 painter->setPen( standardRoutePen );
276 painter->setBrush( QBrush( m_marbleWidget->model()->routingManager()->routeColorAlternative() ) );
278 if ( !m_dropStopOver.isNull() ) {
279 int dx = 1 + m_pixmapSize.width() / 2;
280 int dy = 1 + m_pixmapSize.height() / 2;
281 QPoint center = m_dropStopOver - QPoint( dx, dy );
282 painter->drawPixmap( center, m_targetPixmap );
284 if ( !m_dragStopOver.isNull() && m_dragStopOverRightIndex >= 0 && m_dragStopOverRightIndex <= m_routeRequest->size() ) {
285 QPoint moved = m_dropStopOver - m_dragStopOver;
286 if ( moved.manhattanLength() > 10 ) {
287 qreal lon( 0.0 ), lat( 0.0 );
288 if ( m_marbleWidget->geoCoordinates( m_dropStopOver.x(), m_dropStopOver.y(),
289 lon, lat, GeoDataCoordinates::Radian ) ) {
290 GeoDataCoordinates drag( lon, lat );
291 standardRoutePen.setStyle( Qt::DotLine );
292 painter->setPen( standardRoutePen );
293 GeoDataLineString lineString;
294 if ( m_dragStopOverRightIndex > 0 ) {
295 lineString << m_routeRequest->at( m_dragStopOverRightIndex-1 );
298 if ( m_dragStopOverRightIndex < m_routeRequest->size() ) {
299 lineString << m_routeRequest->at( m_dragStopOverRightIndex );
301 painter->drawPolyline( lineString );
302 standardRoutePen.setStyle( Qt::SolidLine );
303 painter->setPen( standardRoutePen );
313 m_instructionRegions.clear();
314 for (
int i = 0; i < m_routingModel->rowCount(); ++i ) {
315 QModelIndex index = m_routingModel->index( i, 0 );
316 GeoDataCoordinates pos = index.data( MarblePlacemarkModel::CoordinateRole ).value<GeoDataCoordinates>();
318 painter->setBrush( QBrush( m_marbleWidget->model()->routingManager()->routeColorAlternative() ) );
319 if ( m_selectionModel && m_selectionModel->selection().contains( index ) ) {
320 for (
int j=0; j<m_routingModel->route().size(); ++j ) {
321 const RouteSegment & segment = m_routingModel->route().at( j );
322 if ( segment.maneuver().position() == pos ) {
323 GeoDataLineString currentRoutePoints = segment.path();
325 QPen activeRouteSegmentPen( m_marbleWidget->model()->routingManager()->routeColorHighlighted() );
327 activeRouteSegmentPen.setWidth( 6 );
328 if ( m_routeDirty ) {
329 activeRouteSegmentPen.setStyle( Qt::DotLine );
331 painter->setPen( activeRouteSegmentPen );
332 painter->drawPolyline( currentRoutePoints );
334 painter->setPen( standardRoutePen );
340 QRegion region = painter->regionFromEllipse( pos, 12, 12 );
341 m_instructionRegions.push_front( ModelRegion( index, region ) );
342 painter->drawEllipse( pos, 6, 6 );
344 if( !m_routingModel->deviatedFromRoute() ) {
345 GeoDataCoordinates location = m_routingModel->route().currentSegment().nextRouteSegment().maneuver().position();
346 QString nextInstruction = m_routingModel->route().currentSegment().nextRouteSegment().maneuver().instructionText();
347 if( !nextInstruction.isEmpty() ) {
349 painter->drawEllipse( location, 6, 6 );
355 void RoutingLayerPrivate::renderAnnotations( GeoPainter *painter )
357 if ( !m_selectionModel || m_selectionModel->selection().isEmpty() ) {
362 for (
int i = 0; i < m_routingModel->rowCount(); ++i ) {
363 QModelIndex index = m_routingModel->index( i, 0 );
365 if ( m_selectionModel->selection().contains( index ) ) {
366 bool const smallScreen = MarbleGlobal::getInstance()->profiles() & MarbleGlobal::SmallScreen;
367 GeoDataCoordinates pos = index.data( MarblePlacemarkModel::CoordinateRole ).value<GeoDataCoordinates>();
368 painter->setPen( QColor( Qt::black ) );
370 painter->drawAnnotation( pos, index.data().toString(), QSize( smallScreen ? 240 : 120, 0 ), 10, 30, 5, 5 );
375 void RoutingLayerPrivate::renderRequest( GeoPainter *painter )
378 for (
int i = 0; i < m_routeRequest->size(); ++i ) {
379 GeoDataCoordinates pos = m_routeRequest->at( i );
380 if ( pos.longitude() != 0.0 && pos.latitude() != 0.0 ) {
381 QPixmap pixmap = m_routeRequest->pixmap( i );
382 painter->drawPixmap( pos, pixmap );
383 QRegion region = painter->regionFromRect( pos, pixmap.width(), pixmap.height() );
384 m_regions.push_front( RequestRegion( i, region ) );
389 void RoutingLayerPrivate::storeDragPosition(
const QPoint &pos )
391 m_dragStopOver = pos;
392 m_dragStopOverRightIndex = -1;
394 qreal lon( 0.0 ), lat( 0.0 );
395 if ( m_routeRequest && !pos.isNull()
396 && m_marbleWidget->geoCoordinates( pos.x(), pos.y(), lon, lat, GeoDataCoordinates::Radian ) ) {
397 GeoDataCoordinates waypoint( lon, lat );
398 m_dragStopOverRightIndex = m_routingModel->rightNeighbor( waypoint, m_routeRequest );
402 QColor RoutingLayerPrivate::alphaAdjusted(
const QColor &color,
int alpha )
const
404 QColor result( color );
405 result.setAlpha( alpha );
409 bool RoutingLayerPrivate::handleMouseButtonPress( QMouseEvent *e )
411 foreach(
const RequestRegion ®ion, m_regions ) {
412 if ( region.region.contains( e->pos() ) ) {
413 if ( e->button() == Qt::LeftButton ) {
414 m_movingIndex = region.index;
415 m_dropStopOver = QPoint();
416 m_dragStopOver = QPoint();
418 }
else if ( e->button() == Qt::RightButton ) {
419 m_removeViaPointAction->setEnabled(
true );
420 m_activeMenuIndex = region.index;
421 m_contextMenu->showRmbMenu( e->x(), e->y() );
428 foreach(
const ModelRegion ®ion, m_instructionRegions ) {
429 if ( region.region.contains( e->pos() ) && m_selectionModel ) {
430 if ( e->button() == Qt::LeftButton ) {
431 QItemSelectionModel::SelectionFlag command = QItemSelectionModel::ClearAndSelect;
432 if ( m_selectionModel->isSelected( region.index ) ) {
433 command = QItemSelectionModel::Clear;
435 m_selectionModel->select( region.index, command );
436 m_dropStopOver = e->pos();
437 storeDragPosition( e->pos() );
439 emit q->repaintNeeded();
441 }
else if ( e->button() == Qt::RightButton ) {
442 m_removeViaPointAction->setEnabled(
false );
443 m_contextMenu->showRmbMenu( e->x(), e->y() );
450 if ( m_routeRegion.contains( e->pos() ) ) {
451 if ( e->button() == Qt::LeftButton ) {
453 m_dropStopOver = e->pos();
454 storeDragPosition( e->pos() );
456 }
else if ( e->button() == Qt::RightButton ) {
457 m_removeViaPointAction->setEnabled(
false );
458 m_contextMenu->showRmbMenu( e->x(), e->y() );
464 if ( e->button() != Qt::LeftButton ) {
468 foreach(
const RequestRegion ®ion, m_alternativeRouteRegions ) {
469 if ( region.region.contains( e->pos() ) ) {
470 m_alternativeRoutesModel->setCurrentRoute( region.index );
475 foreach(
const ModelRegion ®ion, m_placemarks ) {
476 if ( region.region.contains( e->pos() ) ) {
477 emit q->placemarkSelected( region.index );
485 bool RoutingLayerPrivate::handleMouseButtonRelease( QMouseEvent *e )
487 if ( e->button() != Qt::LeftButton ) {
491 if ( m_movingIndex >= 0 ) {
494 m_marbleWidget->model()->routingManager()->retrieveRoute();
498 if ( !m_dropStopOver.isNull() && !m_dragStopOver.isNull() ) {
499 QPoint moved = e->pos() - m_dragStopOver;
500 if ( moved.manhattanLength() < 10 ) {
504 qreal lon( 0.0 ), lat( 0.0 );
505 if ( m_dragStopOverRightIndex >= 0 && m_dragStopOverRightIndex <= m_routeRequest->size()
506 && m_marbleWidget->geoCoordinates( m_dropStopOver.x(), m_dropStopOver.y(), lon, lat, GeoDataCoordinates::Radian ) ) {
507 GeoDataCoordinates position( lon, lat );
508 m_dragStopOverRightIndex = viaInsertPosition( e->modifiers() );
509 m_routeRequest->insert( m_dragStopOverRightIndex, position );
511 m_marbleWidget->model()->routingManager()->retrieveRoute();
519 bool RoutingLayerPrivate::handleMouseMove( QMouseEvent *e )
521 qreal lon( 0.0 ), lat( 0.0 );
522 if ( m_marbleWidget->geoCoordinates( e->pos().x(), e->pos().y(),
523 lon, lat, GeoDataCoordinates::Radian ) ) {
525 if ( m_movingIndex >= 0 ) {
526 GeoDataCoordinates moved( lon, lat );
527 m_routeRequest->setPosition( m_movingIndex, moved );
528 m_marbleWidget->setCursor( Qt::ArrowCursor );
529 }
else if ( !m_dragStopOver.isNull() ) {
531 m_dragStopOverRightIndex = viaInsertPosition( e->modifiers() );
532 QRect dirty = m_routeRegion.boundingRect();
533 dirty |= QRect( m_dropStopOver, m_pixmapSize );
534 dirty |= QRect( e->pos(), m_pixmapSize );
535 if ( e->buttons() & Qt::LeftButton ) {
536 m_dropStopOver = e->pos();
538 m_dragStopOver = QPoint();
539 m_dropStopOver = QPoint();
541 emit q->repaintNeeded( dirty );
542 m_marbleWidget->setCursor( Qt::ArrowCursor );
543 }
else if ( isInfoPoint( e->pos() ) ) {
545 m_marbleWidget->setCursor( Qt::ArrowCursor );
546 }
else if ( m_routeRegion.contains( e->pos() ) ) {
547 m_dropStopOver = e->pos();
548 m_marbleWidget->setCursor( Qt::ArrowCursor );
549 }
else if ( !m_dropStopOver.isNull() ) {
551 }
else if ( isAlternativeRoutePoint( e->pos() ) ) {
552 m_marbleWidget->setCursor( Qt::ArrowCursor );
559 paintStopOver( QRect( e->pos(), m_pixmapSize ) );
566 bool RoutingLayerPrivate::isInfoPoint(
const QPoint &point )
568 foreach(
const RequestRegion ®ion, m_regions ) {
569 if ( region.region.contains( point ) ) {
574 foreach(
const ModelRegion ®ion, m_instructionRegions ) {
575 if ( region.region.contains( point ) ) {
583 bool RoutingLayerPrivate::isAlternativeRoutePoint(
const QPoint &point )
585 foreach(
const RequestRegion ®ion, m_alternativeRouteRegions ) {
586 if ( region.region.contains( point ) ) {
594 void RoutingLayerPrivate::paintStopOver( QRect dirty )
596 emit q->repaintNeeded( m_dirtyRect );
597 int dx = 1 + m_pixmapSize.width() / 2;
598 int dy = 1 + m_pixmapSize.height() / 2;
599 dirty.adjust( -dx, -dy, -dx, -dy );
600 emit q->repaintNeeded( dirty );
604 void RoutingLayerPrivate::clearStopOver()
606 m_dropStopOver = QPoint();
607 m_dragStopOver = QPoint();
608 emit q->repaintNeeded( m_dirtyRect );
612 QObject( parent ), d( new RoutingLayerPrivate( this, widget ) )
617 this, SLOT(setViewportChanged()) );
619 this, SLOT(setViewportChanged()) );
623 this, SLOT(showAlternativeRoutes()) );
633 return QStringList() <<
"HOVERS_ABOVE_SURFACE";
645 Q_UNUSED( renderPos )
650 if ( d->m_placemarkModel) {
651 d->renderPlacemarks( painter );
654 if ( d->m_alternativeRoutesModel ) {
655 d->renderAlternativeRoutes( painter );
658 d->renderRoute( painter );
660 if ( d->m_routeRequest) {
661 d->renderRequest( painter );
664 d->renderAnnotations( painter );
667 if ( d->m_viewportChanged && d->m_viewContext ==
Still ) {
668 d->m_viewportChanged =
false;
677 if ( event->type() == QEvent::MouseButtonPress ) {
678 QMouseEvent *e =
static_cast<QMouseEvent*
>( event );
679 return d->handleMouseButtonPress( e );
682 if ( event->type() == QEvent::MouseButtonRelease ) {
683 QMouseEvent *e =
static_cast<QMouseEvent*
>( event );
684 return d->handleMouseButtonRelease( e );
687 if ( event->type() == QEvent::MouseMove ) {
688 QMouseEvent *e =
static_cast<QMouseEvent*
>( event );
689 return d->handleMouseMove( e );
697 d->m_placemarkModel = model;
698 setViewportChanged();
703 d->m_selectionModel = selection;
706 void RoutingLayerPrivate::setRouteDirty(
bool dirty )
708 m_routeDirty = dirty;
714 emit q->repaintNeeded();
717 void RoutingLayer::removeViaPoint()
719 if ( d->m_activeMenuIndex >= 0 ) {
720 d->m_routeRequest->remove( d->m_activeMenuIndex );
721 d->m_activeMenuIndex = -1;
722 d->setRouteDirty(
true );
723 d->m_marbleWidget->model()->routingManager()->retrieveRoute();
727 void RoutingLayer::showAlternativeRoutes()
729 setViewportChanged();
733 void RoutingLayer::exportRoute()
735 QString fileName = QFileDialog::getSaveFileName( d->m_marbleWidget,
736 tr(
"Export Route" ),
738 tr(
"GPX and KML files (*.gpx *.kml)" ) );
740 if ( !fileName.isEmpty() ) {
741 if ( fileName.endsWith( QLatin1String(
".gpx" ), Qt::CaseInsensitive ) ) {
742 QFile gpx( fileName );
743 if ( gpx.open( QFile::WriteOnly) ) {
744 d->m_routingModel->exportGpx( &gpx );
748 d->m_marbleWidget->model()->routingManager()->saveRoute( fileName );
756 setViewportChanged();
759 void RoutingLayer::setViewportChanged()
761 d->m_viewportChanged =
true;
762 d->m_routeRegion = QRegion();
763 d->m_instructionRegions.clear();
764 d->m_alternativeRouteRegions.clear();
769 d->m_viewContext = viewContext;
774 #include "RoutingLayer.moc"
This class represents a model of all place marks which are currently available through a given Placem...
A container for Features, Styles and in the future Schemas.
bool eventFilter(QObject *obj, QEvent *event)
Overriding QWidget, used to make the layer interactive.
void setViewContext(ViewContext viewContext)
Set the view context to determine whether the map is used interactively.
void repaintNeeded(const QRect &rect=QRect())
A painter that allows to draw geometric primitives on the map.
qreal zValue() const
Reimplemented from LayerInterface.
This file contains the headers for MarbleModel.
ViewContext
This enum is used to choose context in which map quality gets used.
~RoutingLayer()
Destructor.
Layer of a GeoScene document.
animated view (e.g. while rotating the globe)
RoutingManager * routingManager()
QStringList renderPosition() const
Reimplemented from LayerInterface.
bool render(GeoPainter *painter, ViewportParams *viewport, const QString &renderPos="NONE", GeoSceneLayer *layer=0)
Reimplemented from LayerInterface.
A public class that controls what is visible in the viewport of a Marble map.
static MarbleGlobal * getInstance()
void setPlacemarkModel(MarblePlacemarkModel *model)
Set the placemark model to use.
void synchronizeWith(QItemSelectionModel *selection)
Set the proxy model another QAbstractItemView uses that should share its selection model with us...
A class that defines a 3D bounding box for geographic data.
AlternativeRoutesModel * alternativeRoutesModel()
Provides access to the model which contains a list of alternative routes.