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;
68 MarbleWidget *
const m_marbleWidget;
82 int m_dragStopOverRightIndex;
84 RoutingModel *
const m_routingModel;
86 MarblePlacemarkModel *m_placemarkModel =
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;
102 ViewContext m_viewContext;
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);
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);
154 inline bool isInfoPoint(
const QPoint &point);
157 inline bool isAlternativeRoutePoint(
const QPoint &point);
160 inline void paintStopOver(
QRect position);
163 inline void clearStopOver();
166RoutingLayerPrivate::RoutingLayerPrivate(RoutingLayer *parent, MarbleWidget *widget)
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);
220 auto pos = data.
value<GeoDataCoordinates>();
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)) {
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 auto 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);
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()) {
353 GeoDataCoordinates
location = m_routingModel->route().currentSegment().nextRouteSegment().maneuver().position();
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;
373 auto pos = index.
data(MarblePlacemarkModel::CoordinateRole).
value<GeoDataCoordinates>();
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) {
385 const GeoDataCoordinates pos = m_routeRequest->at(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)) {
402 GeoDataCoordinates waypoint(lon, lat);
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)) {
531 GeoDataCoordinates position(lon, lat);
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) {
548 GeoDataCoordinates moved(lon, lat);
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;
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 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=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.
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)
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