6#include "RoutingLayer.h"
8#include "GeoDataCoordinates.h"
9#include "GeoDataLineString.h"
10#include "GeoPainter.h"
11#include "MarbleColors.h"
12#include "MarblePlacemarkModel.h"
14#include "MarbleWidgetPopupMenu.h"
15#include "RoutingModel.h"
17#include "RouteRequest.h"
19#include "AlternativeRoutesModel.h"
20#include "RoutingManager.h"
22#include "RenderState.h"
24#include <QAbstractItemModel>
26#include <QItemSelectionModel>
35class RoutingLayerPrivate
42 PaintRegion(
const T &index_,
const QRegion ®ion_ ) :
43 index( index_ ), region( region_ )
49 using ModelRegion = PaintRegion<QModelIndex>;
50 using RequestRegion = PaintRegion<int>;
53 RoutingLayer *
const q;
67 MarbleWidget *
const m_marbleWidget;
81 int m_dragStopOverRightIndex;
83 RoutingModel *
const m_routingModel;
85 MarblePlacemarkModel *m_placemarkModel;
91 RouteRequest *
const m_routeRequest;
93 MarbleWidgetPopupMenu *m_contextMenu;
95 QAction *m_removeViaPointAction;
97 int m_activeMenuIndex;
99 AlternativeRoutesModel *
const m_alternativeRoutesModel;
101 ViewContext m_viewContext;
103 bool m_viewportChanged;
105 bool m_isInteractive;
108 explicit RoutingLayerPrivate( RoutingLayer *parent, MarbleWidget *widget );
111 void storeDragPosition(
const QPoint &position );
118 static inline QColor alphaAdjusted(
const QColor &color,
int alpha );
129 inline void renderPlacemarks( GeoPainter *painter );
132 inline void renderRoute( GeoPainter *painter );
135 inline void renderAnnotations( GeoPainter *painter )
const;
138 inline void renderAlternativeRoutes( GeoPainter *painter );
141 inline void renderRequest( GeoPainter *painter );
144 inline bool handleMouseButtonRelease(
QMouseEvent *e );
147 inline bool handleMouseButtonPress(
QMouseEvent *e );
153 inline bool isInfoPoint(
const QPoint &point );
156 inline bool isAlternativeRoutePoint(
const QPoint &point );
159 inline void paintStopOver(
QRect position );
162 inline void clearStopOver();
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 )
184 m_contextMenu =
new MarbleWidgetPopupMenu( m_marbleWidget, m_marbleWidget->model() );
186 QObject::connect( m_removeViaPointAction, SIGNAL(triggered()), q, SLOT(removeViaPoint()) );
189 QObject::connect( exportAction, SIGNAL(triggered()), q, SLOT(exportRoute()) );
191 if ( MarbleGlobal::getInstance()->profiles() & MarbleGlobal::SmallScreen ) {
192 m_pixmapSize =
QSize( 38, 38 );
199 bool leftHand = m_routeRequest->size() / 2 >= m_dragStopOverRightIndex;
200 if ( leftHand && m_routeRequest->size() > 2 ) {
203 return m_routeRequest->size();
206 return m_dragStopOverRightIndex;
210void RoutingLayerPrivate::renderPlacemarks( GeoPainter *painter )
212 m_placemarks.clear();
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 );
218 GeoDataCoordinates pos = data.
value<GeoDataCoordinates>();
221 if ( !pixmap.isNull() && m_selectionModel->isSelected( index ) ) {
224 painter->drawPixmap( pos, result );
226 painter->drawPixmap( pos, pixmap );
229 const QRegion region = painter->regionFromPixmapRect(pos, m_targetPixmap.width(), m_targetPixmap.height());
230 m_placemarks.push_back( ModelRegion( index, region ) );
235void RoutingLayerPrivate::renderAlternativeRoutes( GeoPainter *painter )
237 QPen alternativeRoutePen( m_marbleWidget->model()->routingManager()->routeColorAlternative() );
238 alternativeRoutePen.setWidth( 5 );
239 painter->setPen( alternativeRoutePen );
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 );
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 ) );
256void RoutingLayerPrivate::renderRoute( GeoPainter *painter )
258 GeoDataLineString waypoints = m_routingModel->route().path();
260 QPen standardRoutePen( m_marbleWidget->model()->routingManager()->routeColorStandard() );
261 standardRoutePen.setWidth( 5 );
262 if ( m_marbleWidget->model()->routingManager()->state() == RoutingManager::Downloading ) {
265 painter->setPen( standardRoutePen );
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 );
276 standardRoutePen.setWidth( 2 );
277 painter->setPen( standardRoutePen );
283 painter->setBrush(
QBrush( m_marbleWidget->model()->routingManager()->routeColorAlternative() ) );
285 if ( !m_dropStopOver.isNull() ) {
286 int dx = 1 + m_pixmapSize.width() / 2;
287 int dy = 1 + m_pixmapSize.height() / 2;
289 painter->drawPixmap( center, m_targetPixmap );
291 if ( !m_dragStopOver.isNull() && m_dragStopOverRightIndex >= 0 && m_dragStopOverRightIndex <= m_routeRequest->size() ) {
292 QPoint moved = m_dropStopOver - m_dragStopOver;
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 );
299 painter->setPen( standardRoutePen );
300 GeoDataLineString lineString;
301 if ( m_dragStopOverRightIndex > 0 ) {
302 lineString << m_routeRequest->at( m_dragStopOverRightIndex-1 );
305 if ( m_dragStopOverRightIndex < m_routeRequest->size() ) {
306 lineString << m_routeRequest->at( m_dragStopOverRightIndex );
308 painter->drawPolyline( lineString );
310 painter->setPen( standardRoutePen );
316 if ( m_viewContext == Animation ) {
320 if( m_routingModel->rowCount() == m_routingModel->route().size() ) {
321 m_instructionRegions.clear();
323 QPen activeRouteSegmentPen(m_marbleWidget->model()->routingManager()->routeColorHighlighted());
324 activeRouteSegmentPen.setWidth(6);
325 if (m_marbleWidget->model()->routingManager()->state() == RoutingManager::Downloading) {
328 painter->setPen(activeRouteSegmentPen);
330 for (
int i = 0; i < m_routingModel->rowCount(); ++i ) {
332 GeoDataCoordinates pos = index.
data( MarblePlacemarkModel::CoordinateRole ).
value<GeoDataCoordinates>();
334 if ( m_selectionModel && m_selectionModel->selection().contains( index ) ) {
335 const RouteSegment &segment = m_routingModel->route().at( i );
336 const GeoDataLineString currentRoutePoints = segment.path();
338 painter->drawPolyline( currentRoutePoints );
340 painter->drawPixmap(pos, m_activeRoutePoint);
343 painter->drawPixmap(pos, m_standardRoutePoint);
346 if ( m_isInteractive ) {
347 QRegion region = painter->regionFromEllipse( pos, 12, 12 );
348 m_instructionRegions.push_front( ModelRegion( index, region ) );
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);
362void RoutingLayerPrivate::renderAnnotations( GeoPainter *painter )
const
364 if ( !m_selectionModel || m_selectionModel->selection().isEmpty() ) {
369 for (
int i = 0; i < m_routingModel->rowCount(); ++i ) {
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>();
376 painter->setBrush(
QBrush( Oxygen::sunYellow6 ) );
377 painter->drawAnnotation( pos, index.
data().
toString(),
QSize( smallScreen ? 240 : 120, 0 ), 10, 30, 5, 5 );
382void RoutingLayerPrivate::renderRequest( GeoPainter *painter )
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 ) );
396void RoutingLayerPrivate::storeDragPosition(
const QPoint &pos )
398 m_dragStopOver = pos;
399 m_dragStopOverRightIndex = -1;
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 );
409QColor RoutingLayerPrivate::alphaAdjusted(
const QColor &color,
int alpha )
412 result.setAlpha( alpha );
416QPixmap RoutingLayerPrivate::createRoutePoint(
const QColor &penColor,
const QColor &brushColor)
421 const QBrush brush(brushColor);
429 painter.setBrush(brush);
430 painter.drawEllipse(1, 1, 6, 6);
435bool RoutingLayerPrivate::handleMouseButtonPress(
QMouseEvent *e )
437 for(
const RequestRegion ®ion: m_regions ) {
440 m_movingIndex = region.index;
441 m_dropStopOver =
QPoint();
442 m_dragStopOver =
QPoint();
445 m_removeViaPointAction->setEnabled(
true );
446 m_activeMenuIndex = region.index;
447 m_contextMenu->showRmbMenu( e->
x(), e->
y() );
454 for(
const ModelRegion ®ion: m_instructionRegions ) {
455 if ( region.region.
contains( e->
pos() ) && m_selectionModel ) {
458 if ( m_selectionModel->isSelected( region.index ) ) {
461 m_selectionModel->select( region.index, command );
462 m_dropStopOver = e->
pos();
463 storeDragPosition( e->
pos() );
465 emit q->repaintNeeded();
468 m_removeViaPointAction->setEnabled(
false );
469 m_contextMenu->showRmbMenu( e->
x(), e->
y() );
476 if ( m_routeRegion.contains( e->
pos() ) ) {
479 m_dropStopOver = e->
pos();
480 storeDragPosition( e->
pos() );
483 m_removeViaPointAction->setEnabled(
false );
484 m_contextMenu->showRmbMenu( e->
x(), e->
y() );
494 for(
const RequestRegion ®ion: m_alternativeRouteRegions ) {
496 m_alternativeRoutesModel->setCurrentRoute( region.index );
501 for(
const ModelRegion ®ion: m_placemarks ) {
503 emit q->placemarkSelected( region.index );
511bool RoutingLayerPrivate::handleMouseButtonRelease(
QMouseEvent *e )
517 if ( m_movingIndex >= 0 ) {
520 m_marbleWidget->model()->routingManager()->retrieveRoute();
524 if ( !m_dropStopOver.isNull() && !m_dragStopOver.isNull() ) {
525 QPoint moved = e->
pos() - m_dragStopOver;
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 );
537 m_marbleWidget->model()->routingManager()->retrieveRoute();
545bool RoutingLayerPrivate::handleMouseMove(
QMouseEvent *e )
547 qreal lon( 0.0 ), lat( 0.0 );
548 if ( m_marbleWidget->geoCoordinates( e->
pos().
x(), e->
pos().
y(),
549 lon, lat, GeoDataCoordinates::Radian ) ) {
551 if ( m_movingIndex >= 0 ) {
552 GeoDataCoordinates moved( lon, lat );
553 m_routeRequest->setPosition( m_movingIndex, moved );
555 }
else if ( !m_dragStopOver.isNull() ) {
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 );
562 m_dropStopOver = e->
pos();
564 m_dragStopOver =
QPoint();
565 m_dropStopOver =
QPoint();
567 emit q->repaintNeeded( dirty );
569 }
else if ( isInfoPoint( e->
pos() ) ) {
572 }
else if ( m_routeRegion.contains( e->
pos() ) ) {
573 m_dropStopOver = e->
pos();
575 }
else if ( !m_dropStopOver.isNull() ) {
577 }
else if ( isAlternativeRoutePoint( e->
pos() ) ) {
585 paintStopOver(
QRect( e->
pos(), m_pixmapSize ) );
592bool RoutingLayerPrivate::isInfoPoint(
const QPoint &point )
594 for(
const RequestRegion ®ion: m_regions ) {
595 if ( region.region.
contains( point ) ) {
600 for(
const ModelRegion ®ion: m_instructionRegions ) {
601 if ( region.region.
contains( point ) ) {
609 bool RoutingLayerPrivate::isAlternativeRoutePoint(
const QPoint &point )
611 for(
const RequestRegion ®ion: m_alternativeRouteRegions ) {
612 if ( region.region.
contains( point ) ) {
620void RoutingLayerPrivate::paintStopOver(
QRect dirty )
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 );
630void RoutingLayerPrivate::clearStopOver()
632 m_dropStopOver =
QPoint();
633 m_dragStopOver =
QPoint();
634 emit q->repaintNeeded( m_dirtyRect );
638 QObject( parent ), d( new RoutingLayerPrivate( this, widget ) )
640 connect( widget->
model()->routingManager(),
SIGNAL(stateChanged(RoutingManager::State)),
641 this,
SLOT(updateRouteState()) );
643 this,
SLOT(setViewportChanged()) );
645 this,
SLOT(setViewportChanged()) );
647 this,
SIGNAL(repaintNeeded()) );
649 this,
SLOT(showAlternativeRoutes()) );
659 return QStringList(QStringLiteral(
"HOVERS_ABOVE_SURFACE"));
676 if ( d->m_placemarkModel) {
677 d->renderPlacemarks( painter );
680 if ( d->m_alternativeRoutesModel ) {
681 d->renderAlternativeRoutes( painter );
684 d->renderRoute( painter );
686 if ( d->m_routeRequest) {
687 d->renderRequest( painter );
690 d->renderAnnotations( painter );
693 if ( d->m_viewportChanged && d->m_viewContext ==
Still ) {
694 d->m_viewportChanged =
false;
699RenderState RoutingLayer::renderState()
const
701 return RenderState(QStringLiteral(
"Routing"), d->m_marbleWidget->
model()->routingManager()->state() == RoutingManager::Downloading ?
WaitingForUpdate :
Complete);
708 if ( !d->m_isInteractive ) {
714 return d->handleMouseButtonPress( e );
719 return d->handleMouseButtonRelease( e );
724 return d->handleMouseMove( e );
732 d->m_placemarkModel = model;
733 setViewportChanged();
738 d->m_selectionModel = selection;
741void RoutingLayer::removeViaPoint()
743 if ( d->m_activeMenuIndex >= 0 ) {
744 d->m_routeRequest->
remove( d->m_activeMenuIndex );
745 d->m_activeMenuIndex = -1;
746 emit repaintNeeded();
751void RoutingLayer::showAlternativeRoutes()
753 setViewportChanged();
754 emit repaintNeeded();
757void RoutingLayer::exportRoute()
760 tr(
"Export Route" ),
762 tr(
"GPX and KML files (*.gpx *.kml)" ) );
768 d->m_routingModel->exportGpx( &
gpx );
772 d->m_marbleWidget->
model()->routingManager()->
saveRoute( fileName );
777void RoutingLayer::updateRouteState()
779 setViewportChanged();
780 emit repaintNeeded();
783void RoutingLayer::setViewportChanged()
785 d->m_viewportChanged =
true;
787 d->m_instructionRegions.
clear();
788 d->m_alternativeRouteRegions.
clear();
793 d->m_viewContext = viewContext;
798 d->m_isInteractive = interactive;
803 return d->m_isInteractive;
806QString RoutingLayer::runtimeTrace()
const
808 return QStringLiteral(
"Routing Layer");
813#include "moc_RoutingLayer.cpp"
This file contains the headers for MarbleModel.
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.
Layer of a GeoScene document.
This class represents a model of all place marks which are currently available through a given Placem...
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.
@ Complete
All data is there and up to date.
@ WaitingForUpdate
Rendering is based on complete, but outdated data, data update was requested.
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
QVariant data(int role) const const
bool isValid() 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)
bool isNull() const const
int manhattanLength() const const
void adjust(int dx1, int dy1, int dx2, int dy2)
bool contains(const QPoint &p) const const
bool endsWith(QChar c, Qt::CaseSensitivity cs) const const
bool isEmpty() const const
typedef KeyboardModifiers
QTextStream & center(QTextStream &stream)
bool isNull() const const
QString toString() const const