6#include "RoutingLayer.h"
8#include "AlternativeRoutesModel.h"
9#include "GeoDataCoordinates.h"
10#include "GeoDataLineString.h"
11#include "GeoPainter.h"
13#include "MarbleColors.h"
15#include "MarblePlacemarkModel.h"
17#include "MarbleWidgetPopupMenu.h"
18#include "RenderState.h"
20#include "RouteRequest.h"
21#include "RoutingManager.h"
22#include "RoutingModel.h"
24#include <QAbstractItemModel>
28#include <QItemSelectionModel>
35class RoutingLayerPrivate
42 PaintRegion(
const T &index_,
const QRegion ®ion_)
50 using ModelRegion = PaintRegion<QModelIndex>;
51 using RequestRegion = PaintRegion<int>;
54 RoutingLayer *
const q;
56 QList<ModelRegion> m_instructionRegions;
58 QList<RequestRegion> m_regions;
60 QList<RequestRegion> m_alternativeRouteRegions;
62 QList<ModelRegion> m_placemarks;
64 QRegion m_routeRegion;
68 MarbleWidget *
const m_marbleWidget;
70 QPixmap m_targetPixmap;
72 QPixmap m_standardRoutePoint;
74 QPixmap m_activeRoutePoint;
78 QPoint m_dropStopOver;
80 QPoint m_dragStopOver;
82 int m_dragStopOverRightIndex;
84 RoutingModel *
const m_routingModel;
86 MarblePlacemarkModel *m_placemarkModel =
nullptr;
88 QItemSelectionModel *m_selectionModel =
nullptr;
92 RouteRequest *
const m_routeRequest;
94 MarbleWidgetPopupMenu *m_contextMenu =
nullptr;
96 QAction *m_removeViaPointAction =
nullptr;
98 int m_activeMenuIndex;
100 AlternativeRoutesModel *
const m_alternativeRoutesModel;
104 bool m_viewportChanged;
106 bool m_isInteractive;
109 explicit RoutingLayerPrivate(RoutingLayer *parent, MarbleWidget *widget);
112 void storeDragPosition(
const QPoint &position);
119 static inline QColor alphaAdjusted(
const QColor &color,
int alpha);
121 static QPixmap createRoutePoint(
const QColor &penColor,
const QColor &brushColor);
130 inline void renderPlacemarks(GeoPainter *painter);
133 inline void renderRoute(GeoPainter *painter);
136 inline void renderAnnotations(GeoPainter *painter)
const;
139 inline void renderAlternativeRoutes(GeoPainter *painter);
142 inline void renderRequest(GeoPainter *painter);
145 inline bool handleMouseButtonRelease(QMouseEvent *e);
148 inline bool handleMouseButtonPress(QMouseEvent *e);
151 inline bool handleMouseMove(QMouseEvent *e);
154 inline bool isInfoPoint(
const QPoint &point);
157 inline bool isAlternativeRoutePoint(
const QPoint &point);
160 inline void paintStopOver(QRect position);
163 inline void clearStopOver();
169 , m_marbleWidget(widget)
170 , m_targetPixmap(QStringLiteral(
":/data/bitmaps/routing_pick.png"))
171 , m_standardRoutePoint(
172 createRoutePoint(widget->model()->routingManager()->routeColorStandard(), widget->model()->routingManager()->routeColorAlternative()))
173 , m_activeRoutePoint(createRoutePoint(widget->model()->routingManager()->routeColorHighlighted(), alphaAdjusted(Oxygen::hotOrange4, 200)))
174 , m_dragStopOverRightIndex(-1)
175 , m_routingModel(widget->model()->routingManager()->routingModel())
176 , m_placemarkModel(nullptr)
177 , m_selectionModel(nullptr)
178 , m_pixmapSize(22, 22)
179 , m_routeRequest(widget->model()->routingManager()->routeRequest())
180 , m_activeMenuIndex(-1)
181 , m_alternativeRoutesModel(widget->model()->routingManager()->alternativeRoutesModel())
182 , m_viewContext(
Still)
183 , m_viewportChanged(true)
184 , m_isInteractive(true)
186 m_contextMenu =
new MarbleWidgetPopupMenu(m_marbleWidget, m_marbleWidget->model());
188 QObject::connect(m_removeViaPointAction, SIGNAL(triggered()), q, SLOT(removeViaPoint()));
191 QObject::connect(exportAction, SIGNAL(triggered()), q, SLOT(exportRoute()));
193 if (MarbleGlobal::getInstance()->profiles() & MarbleGlobal::SmallScreen) {
194 m_pixmapSize =
QSize(38, 38);
201 bool leftHand = m_routeRequest->size() / 2 >= m_dragStopOverRightIndex;
202 if (leftHand && m_routeRequest->size() > 2) {
205 return m_routeRequest->size();
208 return m_dragStopOverRightIndex;
212void RoutingLayerPrivate::renderPlacemarks(GeoPainter *painter)
214 m_placemarks.clear();
216 for (
int i = 0; i < m_placemarkModel->rowCount(); ++i) {
218 QVariant data = index.
data(MarblePlacemarkModel::CoordinateRole);
223 if (!pixmap.isNull() && m_selectionModel->isSelected(index)) {
226 painter->drawPixmap(pos, result);
228 painter->drawPixmap(pos, pixmap);
231 const QRegion region = painter->regionFromPixmapRect(pos, m_targetPixmap.width(), m_targetPixmap.height());
232 m_placemarks.push_back(ModelRegion(index, region));
237void RoutingLayerPrivate::renderAlternativeRoutes(GeoPainter *painter)
239 QPen alternativeRoutePen(m_marbleWidget->model()->routingManager()->routeColorAlternative());
240 alternativeRoutePen.setWidth(5);
241 painter->setPen(alternativeRoutePen);
243 for (
int i = 0; i < m_alternativeRoutesModel->rowCount(); ++i) {
244 const GeoDataDocument *route = m_alternativeRoutesModel->route(i);
245 if (route && route != m_alternativeRoutesModel->currentRoute()) {
246 const GeoDataLineString *points = AlternativeRoutesModel::waypoints(route);
248 painter->drawPolyline(*points);
249 if (m_viewportChanged && m_isInteractive && m_viewContext == Still) {
250 QRegion region = painter->regionFromPolyline(*points, 8);
251 m_alternativeRouteRegions.push_back(RequestRegion(i, region));
258void RoutingLayerPrivate::renderRoute(GeoPainter *painter)
260 GeoDataLineString waypoints = m_routingModel->route().path();
262 QPen standardRoutePen(m_marbleWidget->model()->routingManager()->routeColorStandard());
263 standardRoutePen.setWidth(5);
264 if (m_marbleWidget->model()->routingManager()->state() == RoutingManager::Downloading) {
267 painter->setPen(standardRoutePen);
269 painter->drawPolyline(waypoints);
270 if (m_viewportChanged && m_viewContext == Still) {
271 int const offset = MarbleGlobal::getInstance()->profiles() & MarbleGlobal::SmallScreen ? 24 : 8;
272 if (m_isInteractive) {
273 m_routeRegion = painter->regionFromPolyline(waypoints, offset);
277 standardRoutePen.setWidth(2);
278 painter->setPen(standardRoutePen);
284 painter->setBrush(
QBrush(m_marbleWidget->model()->routingManager()->routeColorAlternative()));
286 if (!m_dropStopOver.isNull()) {
287 int dx = 1 + m_pixmapSize.width() / 2;
288 int dy = 1 + m_pixmapSize.height() / 2;
290 painter->drawPixmap(center, m_targetPixmap);
292 if (!m_dragStopOver.isNull() && m_dragStopOverRightIndex >= 0 && m_dragStopOverRightIndex <= m_routeRequest->size()) {
293 QPoint moved = m_dropStopOver - m_dragStopOver;
295 qreal lon(0.0), lat(0.0);
296 if (m_marbleWidget->geoCoordinates(m_dropStopOver.x(), m_dropStopOver.y(), lon, lat, GeoDataCoordinates::Radian)) {
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) {
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);
342 painter->drawPixmap(pos, m_standardRoutePoint);
345 if (m_isInteractive) {
346 QRegion region = painter->regionFromEllipse(pos, 12, 12);
347 m_instructionRegions.push_front(ModelRegion(index, region));
352 if (!m_routingModel->deviatedFromRoute()) {
354 QString nextInstruction = m_routingModel->route().currentSegment().nextRouteSegment().maneuver().instructionText();
355 if (!nextInstruction.
isEmpty()) {
356 painter->drawPixmap(location, m_activeRoutePoint);
361void RoutingLayerPrivate::renderAnnotations(GeoPainter *painter)
const
363 if (!m_selectionModel || m_selectionModel->selection().isEmpty()) {
368 for (
int i = 0; i < m_routingModel->rowCount(); ++i) {
371 if (m_selectionModel->selection().contains(index)) {
372 bool const smallScreen = MarbleGlobal::getInstance()->profiles() & MarbleGlobal::SmallScreen;
375 painter->setBrush(
QBrush(Oxygen::sunYellow6));
376 painter->drawAnnotation(pos, index.
data().
toString(),
QSize(smallScreen ? 240 : 120, 0), 10, 30, 5, 5);
381void RoutingLayerPrivate::renderRequest(GeoPainter *painter)
384 for (
int i = 0; i < m_routeRequest->size(); ++i) {
387 QPixmap pixmap = m_routeRequest->pixmap(i);
388 painter->drawPixmap(pos, pixmap);
389 const QRegion region = painter->regionFromPixmapRect(pos, pixmap.
width(), pixmap.
height());
390 m_regions.push_front(RequestRegion(i, region));
395void RoutingLayerPrivate::storeDragPosition(
const QPoint &pos)
397 m_dragStopOver = pos;
398 m_dragStopOverRightIndex = -1;
400 qreal lon(0.0), lat(0.0);
401 if (m_routeRequest && !pos.
isNull() && m_marbleWidget->geoCoordinates(pos.
x(), pos.
y(), lon, lat, GeoDataCoordinates::Radian)) {
403 m_dragStopOverRightIndex = m_routingModel->rightNeighbor(waypoint, m_routeRequest);
407QColor RoutingLayerPrivate::alphaAdjusted(
const QColor &color,
int alpha)
410 result.setAlpha(alpha);
414QPixmap RoutingLayerPrivate::createRoutePoint(
const QColor &penColor,
const QColor &brushColor)
419 const QBrush brush(brushColor);
427 painter.setBrush(brush);
428 painter.drawEllipse(1, 1, 6, 6);
433bool RoutingLayerPrivate::handleMouseButtonPress(
QMouseEvent *e)
435 for (
const RequestRegion ®ion : std::as_const(m_regions)) {
438 m_movingIndex = region.index;
439 m_dropStopOver =
QPoint();
440 m_dragStopOver =
QPoint();
443 m_removeViaPointAction->setEnabled(
true);
444 m_activeMenuIndex = region.index;
445 m_contextMenu->showRmbMenu(e->
x(), e->
y());
452 for (
const ModelRegion ®ion : std::as_const(m_instructionRegions)) {
453 if (region.region.
contains(e->
pos()) && m_selectionModel) {
456 if (m_selectionModel->isSelected(region.index)) {
459 m_selectionModel->select(region.index, command);
460 m_dropStopOver = e->
pos();
461 storeDragPosition(e->
pos());
463 Q_EMIT q->repaintNeeded();
466 m_removeViaPointAction->setEnabled(
false);
467 m_contextMenu->showRmbMenu(e->
x(), e->
y());
474 if (m_routeRegion.contains(e->
pos())) {
477 m_dropStopOver = e->
pos();
478 storeDragPosition(e->
pos());
481 m_removeViaPointAction->setEnabled(
false);
482 m_contextMenu->showRmbMenu(e->
x(), e->
y());
492 for (
const RequestRegion ®ion : std::as_const(m_alternativeRouteRegions)) {
494 m_alternativeRoutesModel->setCurrentRoute(region.index);
499 for (
const ModelRegion ®ion : std::as_const(m_placemarks)) {
501 Q_EMIT q->placemarkSelected(region.index);
509bool RoutingLayerPrivate::handleMouseButtonRelease(
QMouseEvent *e)
515 if (m_movingIndex >= 0) {
518 m_marbleWidget->model()->routingManager()->retrieveRoute();
522 if (!m_dropStopOver.isNull() && !m_dragStopOver.isNull()) {
523 QPoint moved = e->
pos() - m_dragStopOver;
528 qreal lon(0.0), lat(0.0);
529 if (m_dragStopOverRightIndex >= 0 && m_dragStopOverRightIndex <= m_routeRequest->size()
530 && m_marbleWidget->geoCoordinates(m_dropStopOver.x(), m_dropStopOver.y(), lon, lat, GeoDataCoordinates::Radian)) {
532 m_dragStopOverRightIndex = viaInsertPosition(e->
modifiers());
533 m_routeRequest->insert(m_dragStopOverRightIndex, position);
535 m_marbleWidget->model()->routingManager()->retrieveRoute();
543bool RoutingLayerPrivate::handleMouseMove(
QMouseEvent *e)
545 qreal lon(0.0), lat(0.0);
546 if (m_marbleWidget->geoCoordinates(e->
pos().
x(), e->
pos().
y(), lon, lat, GeoDataCoordinates::Radian)) {
547 if (m_movingIndex >= 0) {
549 m_routeRequest->setPosition(m_movingIndex, moved);
551 }
else if (!m_dragStopOver.isNull()) {
553 m_dragStopOverRightIndex = viaInsertPosition(e->
modifiers());
554 QRect dirty = m_routeRegion.boundingRect();
555 dirty |=
QRect(m_dropStopOver, m_pixmapSize);
556 dirty |=
QRect(e->
pos(), m_pixmapSize);
558 m_dropStopOver = e->
pos();
560 m_dragStopOver =
QPoint();
561 m_dropStopOver =
QPoint();
563 Q_EMIT q->repaintNeeded(dirty);
565 }
else if (isInfoPoint(e->
pos())) {
568 }
else if (m_routeRegion.contains(e->
pos())) {
569 m_dropStopOver = e->
pos();
571 }
else if (!m_dropStopOver.isNull()) {
573 }
else if (isAlternativeRoutePoint(e->
pos())) {
580 paintStopOver(
QRect(e->
pos(), m_pixmapSize));
587bool RoutingLayerPrivate::isInfoPoint(
const QPoint &point)
589 for (
const RequestRegion ®ion : std::as_const(m_regions)) {
590 if (region.region.
contains(point)) {
595 for (
const ModelRegion ®ion : std::as_const(m_instructionRegions)) {
596 if (region.region.
contains(point)) {
604bool RoutingLayerPrivate::isAlternativeRoutePoint(
const QPoint &point)
606 for (
const RequestRegion ®ion : std::as_const(m_alternativeRouteRegions)) {
607 if (region.region.
contains(point)) {
615void RoutingLayerPrivate::paintStopOver(
QRect dirty)
617 Q_EMIT q->repaintNeeded(m_dirtyRect);
618 int dx = 1 + m_pixmapSize.width() / 2;
619 int dy = 1 + m_pixmapSize.height() / 2;
620 dirty.
adjust(-dx, -dy, -dx, -dy);
621 Q_EMIT q->repaintNeeded(dirty);
625void RoutingLayerPrivate::clearStopOver()
627 m_dropStopOver =
QPoint();
628 m_dragStopOver =
QPoint();
629 Q_EMIT q->repaintNeeded(m_dirtyRect);
634 , d(new RoutingLayerPrivate(this, widget))
636 connect(widget->
model()->routingManager(), SIGNAL(stateChanged(RoutingManager::State)),
this, SLOT(updateRouteState()));
641 SLOT(setViewportChanged()));
653 return QStringList(QStringLiteral(
"HOVERS_ABOVE_SURFACE"));
669 if (d->m_placemarkModel) {
670 d->renderPlacemarks(painter);
673 if (d->m_alternativeRoutesModel) {
674 d->renderAlternativeRoutes(painter);
677 d->renderRoute(painter);
679 if (d->m_routeRequest) {
680 d->renderRequest(painter);
683 d->renderAnnotations(painter);
686 if (d->m_viewportChanged && d->m_viewContext ==
Still) {
687 d->m_viewportChanged =
false;
692RenderState RoutingLayer::renderState()
const
694 return RenderState(QStringLiteral(
"Routing"),
702 if (!d->m_isInteractive) {
708 return d->handleMouseButtonPress(e);
713 return d->handleMouseButtonRelease(e);
718 return d->handleMouseMove(e);
726 d->m_placemarkModel = model;
727 setViewportChanged();
732 d->m_selectionModel = selection;
735void RoutingLayer::removeViaPoint()
737 if (d->m_activeMenuIndex >= 0) {
738 d->m_routeRequest->
remove(d->m_activeMenuIndex);
739 d->m_activeMenuIndex = -1;
745void RoutingLayer::showAlternativeRoutes()
747 setViewportChanged();
751void RoutingLayer::exportRoute()
756 tr(
"GPX and KML files (*.gpx *.kml)"));
762 d->m_routingModel->exportGpx(&gpx);
766 d->m_marbleWidget->model()->routingManager()->saveRoute(fileName);
771void RoutingLayer::updateRouteState()
773 setViewportChanged();
777void RoutingLayer::setViewportChanged()
779 d->m_viewportChanged =
true;
780 d->m_routeRegion = QRegion();
781 d->m_instructionRegions.clear();
782 d->m_alternativeRouteRegions.clear();
787 d->m_viewContext = viewContext;
792 d->m_isInteractive = interactive;
797 return d->m_isInteractive;
800QString RoutingLayer::runtimeTrace()
const
802 return QStringLiteral(
"Routing Layer");
807#include "moc_RoutingLayer.cpp"
This file contains the headers for MarbleModel.
A 3d point representation.
bool isValid() const
Returns.
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.
A paint layer that serves as a view on a route model.
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.
RoutingLayer(MarbleWidget *widget, QWidget *parent=nullptr)
Constructor.
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=QStringLiteral("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.
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)
QObject * parent() const const
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