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 "AlternativeRoutesModel.h"
9#include "GeoDataCoordinates.h"
10#include "GeoDataLineString.h"
11#include "GeoPainter.h"
12#include "Maneuver.h"
13#include "MarbleColors.h"
14#include "MarbleModel.h"
15#include "MarblePlacemarkModel.h"
16#include "MarbleWidget.h"
17#include "MarbleWidgetPopupMenu.h"
18#include "RenderState.h"
19#include "Route.h"
20#include "RouteRequest.h"
21#include "RoutingManager.h"
22#include "RoutingModel.h"
23
24#include <QAbstractItemModel>
25#include <QAction>
26#include <QFileDialog>
27#include <QIcon>
28#include <QItemSelectionModel>
29#include <QMouseEvent>
30#include <QPixmap>
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_)
44 , region(region_)
45 {
46 // nothing to do
47 }
48 };
49
50 using ModelRegion = PaintRegion<QModelIndex>;
51 using RequestRegion = PaintRegion<int>;
52
53public:
54 RoutingLayer *const q;
55
56 QList<ModelRegion> m_instructionRegions;
57
58 QList<RequestRegion> m_regions;
59
60 QList<RequestRegion> m_alternativeRouteRegions;
61
62 QList<ModelRegion> m_placemarks;
63
64 QRegion m_routeRegion;
65
66 int m_movingIndex;
67
68 MarbleWidget *const m_marbleWidget;
69
70 QPixmap m_targetPixmap;
71
72 QPixmap m_standardRoutePoint;
73
74 QPixmap m_activeRoutePoint;
75
76 QRect m_dirtyRect;
77
78 QPoint m_dropStopOver;
79
80 QPoint m_dragStopOver;
81
82 int m_dragStopOverRightIndex;
83
84 RoutingModel *const m_routingModel;
85
86 MarblePlacemarkModel *m_placemarkModel = nullptr;
87
88 QItemSelectionModel *m_selectionModel = nullptr;
89
90 QSize m_pixmapSize;
91
92 RouteRequest *const m_routeRequest;
93
94 MarbleWidgetPopupMenu *m_contextMenu = nullptr;
95
96 QAction *m_removeViaPointAction = nullptr;
97
98 int m_activeMenuIndex;
99
100 AlternativeRoutesModel *const m_alternativeRoutesModel;
101
102 ViewContext m_viewContext;
103
104 bool m_viewportChanged;
105
106 bool m_isInteractive;
107
108 /** Constructor */
109 explicit RoutingLayerPrivate(RoutingLayer *parent, MarbleWidget *widget);
110
111 /** Update the cached drag position. Use an empty point to clear it. */
112 void storeDragPosition(const QPoint &position);
113
114 // The following methods are mostly only called at one place in the code, but often
115 // Inlined to avoid the function call overhead. Having functions here is just to
116 // keep the code clean
117
118 /** Returns the same color as the given one with its alpha channel adjusted to the given value */
119 static inline QColor alphaAdjusted(const QColor &color, int alpha);
120
121 static QPixmap createRoutePoint(const QColor &penColor, const QColor &brushColor);
122
123 /**
124 * Returns the start or destination position if Ctrl key is among the
125 * provided modifiers, the cached insert position otherwise
126 */
127 inline int viaInsertPosition(Qt::KeyboardModifiers modifiers) const;
128
129 /** Paint icons for each placemark in the placemark model */
130 inline void renderPlacemarks(GeoPainter *painter);
131
132 /** Paint waypoint polygon */
133 inline void renderRoute(GeoPainter *painter);
134
135 /** Paint turn instruction for selected items */
136 inline void renderAnnotations(GeoPainter *painter) const;
137
138 /** Paint alternative routes in gray */
139 inline void renderAlternativeRoutes(GeoPainter *painter);
140
141 /** Paint icons for trip points etc */
142 inline void renderRequest(GeoPainter *painter);
143
144 /** Insert via points or Q_EMIT position signal, if appropriate */
145 inline bool handleMouseButtonRelease(QMouseEvent *e);
146
147 /** Select route instructions points, start dragging trip points */
148 inline bool handleMouseButtonPress(QMouseEvent *e);
149
150 /** Dragging trip points, route polygon hovering */
151 inline bool handleMouseMove(QMouseEvent *e);
152
153 /** True if the given point (screen coordinates) is among the route instruction points */
154 inline bool isInfoPoint(const QPoint &point);
155
156 /** True if the given point (screen coordinates) is above an alternative route */
157 inline bool isAlternativeRoutePoint(const QPoint &point);
158
159 /** Paint the stopover indicator pixmap at the given position. Also repaints the old position */
160 inline void paintStopOver(QRect position);
161
162 /** Removes the stopover indicator pixmap. Also repaints its old position */
163 inline void clearStopOver();
164};
165
166RoutingLayerPrivate::RoutingLayerPrivate(RoutingLayer *parent, MarbleWidget *widget)
167 : q(parent)
168 , m_movingIndex(-1)
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)
185{
186 m_contextMenu = new MarbleWidgetPopupMenu(m_marbleWidget, m_marbleWidget->model());
187 m_removeViaPointAction = new QAction(QObject::tr("&Remove this Destination"), q);
188 QObject::connect(m_removeViaPointAction, SIGNAL(triggered()), q, SLOT(removeViaPoint()));
189 m_contextMenu->addAction(Qt::RightButton, m_removeViaPointAction);
190 auto exportAction = new QAction(QObject::tr("&Export Route..."), q);
191 QObject::connect(exportAction, SIGNAL(triggered()), q, SLOT(exportRoute()));
192 m_contextMenu->addAction(Qt::RightButton, exportAction);
193 if (MarbleGlobal::getInstance()->profiles() & MarbleGlobal::SmallScreen) {
194 m_pixmapSize = QSize(38, 38);
195 }
196}
197
198int RoutingLayerPrivate::viaInsertPosition(Qt::KeyboardModifiers modifiers) const
199{
200 if (modifiers & Qt::ControlModifier) {
201 bool leftHand = m_routeRequest->size() / 2 >= m_dragStopOverRightIndex;
202 if (leftHand && m_routeRequest->size() > 2) {
203 return 0;
204 } else {
205 return m_routeRequest->size();
206 }
207 } else {
208 return m_dragStopOverRightIndex;
209 }
210}
211
212void RoutingLayerPrivate::renderPlacemarks(GeoPainter *painter)
213{
214 m_placemarks.clear();
215 painter->setPen(QColor(Qt::black));
216 for (int i = 0; i < m_placemarkModel->rowCount(); ++i) {
217 QModelIndex index = m_placemarkModel->index(i, 0);
218 QVariant data = index.data(MarblePlacemarkModel::CoordinateRole);
219 if (index.isValid() && !data.isNull()) {
220 auto pos = data.value<GeoDataCoordinates>();
221
222 auto pixmap = index.data(Qt::DecorationRole).value<QPixmap>();
223 if (!pixmap.isNull() && m_selectionModel->isSelected(index)) {
224 QIcon selected = QIcon(pixmap);
225 QPixmap result = selected.pixmap(m_pixmapSize, QIcon::Selected, QIcon::On);
226 painter->drawPixmap(pos, result);
227 } else {
228 painter->drawPixmap(pos, pixmap);
229 }
230
231 const QRegion region = painter->regionFromPixmapRect(pos, m_targetPixmap.width(), m_targetPixmap.height());
232 m_placemarks.push_back(ModelRegion(index, region));
233 }
234 }
235}
236
237void RoutingLayerPrivate::renderAlternativeRoutes(GeoPainter *painter)
238{
239 QPen alternativeRoutePen(m_marbleWidget->model()->routingManager()->routeColorAlternative());
240 alternativeRoutePen.setWidth(5);
241 painter->setPen(alternativeRoutePen);
242
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);
247 if (points) {
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));
252 }
253 }
254 }
255 }
256}
257
258void RoutingLayerPrivate::renderRoute(GeoPainter *painter)
259{
260 GeoDataLineString waypoints = m_routingModel->route().path();
261
262 QPen standardRoutePen(m_marbleWidget->model()->routingManager()->routeColorStandard());
263 standardRoutePen.setWidth(5);
264 if (m_marbleWidget->model()->routingManager()->state() == RoutingManager::Downloading) {
265 standardRoutePen.setStyle(Qt::DotLine);
266 }
267 painter->setPen(standardRoutePen);
268
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);
274 }
275 }
276
277 standardRoutePen.setWidth(2);
278 painter->setPen(standardRoutePen);
279
280 // Map matched position
281 // painter->setBrush( QBrush( Oxygen::brickRed4) );
282 // painter->drawEllipse( m_routingModel->route().positionOnRoute(), 8, 8 );
283
284 painter->setBrush(QBrush(m_marbleWidget->model()->routingManager()->routeColorAlternative()));
285
286 if (!m_dropStopOver.isNull()) {
287 int dx = 1 + m_pixmapSize.width() / 2;
288 int dy = 1 + m_pixmapSize.height() / 2;
289 QPoint center = m_dropStopOver - QPoint(dx, dy);
290 painter->drawPixmap(center, m_targetPixmap);
291
292 if (!m_dragStopOver.isNull() && m_dragStopOverRightIndex >= 0 && m_dragStopOverRightIndex <= m_routeRequest->size()) {
293 QPoint moved = m_dropStopOver - m_dragStopOver;
294 if (moved.manhattanLength() > 10) {
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);
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 auto 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 } else {
342 painter->drawPixmap(pos, m_standardRoutePoint);
343 }
344
345 if (m_isInteractive) {
346 QRegion region = painter->regionFromEllipse(pos, 12, 12);
347 m_instructionRegions.push_front(ModelRegion(index, region));
348 }
349 }
350 }
351
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);
357 }
358 }
359}
360
361void RoutingLayerPrivate::renderAnnotations(GeoPainter *painter) const
362{
363 if (!m_selectionModel || m_selectionModel->selection().isEmpty()) {
364 // nothing to do
365 return;
366 }
367
368 for (int i = 0; i < m_routingModel->rowCount(); ++i) {
369 QModelIndex index = m_routingModel->index(i, 0);
370
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>();
374 painter->setPen(QColor(Qt::black));
375 painter->setBrush(QBrush(Oxygen::sunYellow6));
376 painter->drawAnnotation(pos, index.data().toString(), QSize(smallScreen ? 240 : 120, 0), 10, 30, 5, 5);
377 }
378 }
379}
380
381void RoutingLayerPrivate::renderRequest(GeoPainter *painter)
382{
383 m_regions.clear();
384 for (int i = 0; i < m_routeRequest->size(); ++i) {
385 const GeoDataCoordinates pos = m_routeRequest->at(i);
386 if (pos.isValid()) {
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));
391 }
392 }
393}
394
395void RoutingLayerPrivate::storeDragPosition(const QPoint &pos)
396{
397 m_dragStopOver = pos;
398 m_dragStopOverRightIndex = -1;
399
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);
404 }
405}
406
407QColor RoutingLayerPrivate::alphaAdjusted(const QColor &color, int alpha)
408{
409 QColor result(color);
410 result.setAlpha(alpha);
411 return result;
412}
413
414QPixmap RoutingLayerPrivate::createRoutePoint(const QColor &penColor, const QColor &brushColor)
415{
416 QPen pen(penColor);
417 pen.setWidth(2);
418
419 const QBrush brush(brushColor);
420
421 QPixmap routePoint(QSize(8, 8));
422 routePoint.fill(Qt::transparent);
423
424 QPainter painter(&routePoint);
425 painter.setRenderHint(QPainter::Antialiasing, true);
426 painter.setPen(pen);
427 painter.setBrush(brush);
428 painter.drawEllipse(1, 1, 6, 6);
429
430 return routePoint;
431}
432
433bool RoutingLayerPrivate::handleMouseButtonPress(QMouseEvent *e)
434{
435 for (const RequestRegion &region : std::as_const(m_regions)) {
436 if (region.region.contains(e->pos())) {
437 if (e->button() == Qt::LeftButton) {
438 m_movingIndex = region.index;
439 m_dropStopOver = QPoint();
440 m_dragStopOver = QPoint();
441 return true;
442 } else if (e->button() == Qt::RightButton) {
443 m_removeViaPointAction->setEnabled(true);
444 m_activeMenuIndex = region.index;
445 m_contextMenu->showRmbMenu(e->x(), e->y());
446 return true;
447 } else
448 return false;
449 }
450 }
451
452 for (const ModelRegion &region : std::as_const(m_instructionRegions)) {
453 if (region.region.contains(e->pos()) && m_selectionModel) {
454 if (e->button() == Qt::LeftButton) {
456 if (m_selectionModel->isSelected(region.index)) {
458 }
459 m_selectionModel->select(region.index, command);
460 m_dropStopOver = e->pos();
461 storeDragPosition(e->pos());
462 // annotation and old annotation are dirty, large region
463 Q_EMIT q->repaintNeeded();
464 return true;
465 } else if (e->button() == Qt::RightButton) {
466 m_removeViaPointAction->setEnabled(false);
467 m_contextMenu->showRmbMenu(e->x(), e->y());
468 return true;
469 } else
470 return false;
471 }
472 }
473
474 if (m_routeRegion.contains(e->pos())) {
475 if (e->button() == Qt::LeftButton) {
476 /** @todo: Determine the neighbored via points and insert in order */
477 m_dropStopOver = e->pos();
478 storeDragPosition(e->pos());
479 return true;
480 } else if (e->button() == Qt::RightButton) {
481 m_removeViaPointAction->setEnabled(false);
482 m_contextMenu->showRmbMenu(e->x(), e->y());
483 return true;
484 } else
485 return false;
486 }
487
488 if (e->button() != Qt::LeftButton) {
489 return false;
490 }
491
492 for (const RequestRegion &region : std::as_const(m_alternativeRouteRegions)) {
493 if (region.region.contains(e->pos())) {
494 m_alternativeRoutesModel->setCurrentRoute(region.index);
495 return true;
496 }
497 }
498
499 for (const ModelRegion &region : std::as_const(m_placemarks)) {
500 if (region.region.contains(e->pos())) {
501 Q_EMIT q->placemarkSelected(region.index);
502 return true;
503 }
504 }
505
506 return false;
507}
508
509bool RoutingLayerPrivate::handleMouseButtonRelease(QMouseEvent *e)
510{
511 if (e->button() != Qt::LeftButton) {
512 return false;
513 }
514
515 if (m_movingIndex >= 0) {
516 m_movingIndex = -1;
517 clearStopOver();
518 m_marbleWidget->model()->routingManager()->retrieveRoute();
519 return true;
520 }
521
522 if (!m_dropStopOver.isNull() && !m_dragStopOver.isNull()) {
523 QPoint moved = e->pos() - m_dragStopOver;
524 if (moved.manhattanLength() < 10) {
525 return false;
526 }
527
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);
534 clearStopOver();
535 m_marbleWidget->model()->routingManager()->retrieveRoute();
536 return true;
537 }
538 }
539
540 return false;
541}
542
543bool RoutingLayerPrivate::handleMouseMove(QMouseEvent *e)
544{
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);
550 m_marbleWidget->setCursor(Qt::ArrowCursor);
551 } else if (!m_dragStopOver.isNull()) {
552 // Repaint only that region of the map that is affected by the change
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);
557 if (e->buttons() & Qt::LeftButton) {
558 m_dropStopOver = e->pos();
559 } else {
560 m_dragStopOver = QPoint();
561 m_dropStopOver = QPoint();
562 }
563 Q_EMIT q->repaintNeeded(dirty);
564 m_marbleWidget->setCursor(Qt::ArrowCursor);
565 } else if (isInfoPoint(e->pos())) {
566 clearStopOver();
567 m_marbleWidget->setCursor(Qt::ArrowCursor);
568 } else if (m_routeRegion.contains(e->pos())) {
569 m_dropStopOver = e->pos();
570 m_marbleWidget->setCursor(Qt::ArrowCursor);
571 } else if (!m_dropStopOver.isNull()) {
572 clearStopOver();
573 } else if (isAlternativeRoutePoint(e->pos())) {
574 m_marbleWidget->setCursor(Qt::ArrowCursor);
575 } else {
576 return false;
577 }
578
579 // Update pixmap in the map (old and new position needs repaint)
580 paintStopOver(QRect(e->pos(), m_pixmapSize));
581 return true;
582 }
583
584 return false;
585}
586
587bool RoutingLayerPrivate::isInfoPoint(const QPoint &point)
588{
589 for (const RequestRegion &region : std::as_const(m_regions)) {
590 if (region.region.contains(point)) {
591 return true;
592 }
593 }
594
595 for (const ModelRegion &region : std::as_const(m_instructionRegions)) {
596 if (region.region.contains(point)) {
597 return true;
598 }
599 }
600
601 return false;
602}
603
604bool RoutingLayerPrivate::isAlternativeRoutePoint(const QPoint &point)
605{
606 for (const RequestRegion &region : std::as_const(m_alternativeRouteRegions)) {
607 if (region.region.contains(point)) {
608 return true;
609 }
610 }
611
612 return false;
613}
614
615void RoutingLayerPrivate::paintStopOver(QRect dirty)
616{
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);
622 m_dirtyRect = dirty;
623}
624
625void RoutingLayerPrivate::clearStopOver()
626{
627 m_dropStopOver = QPoint();
628 m_dragStopOver = QPoint();
629 Q_EMIT q->repaintNeeded(m_dirtyRect);
630}
631
632RoutingLayer::RoutingLayer(MarbleWidget *widget, QWidget *parent)
633 : QObject(parent)
634 , d(new RoutingLayerPrivate(this, widget))
635{
636 connect(widget->model()->routingManager(), SIGNAL(stateChanged(RoutingManager::State)), this, SLOT(updateRouteState()));
637 connect(widget, SIGNAL(visibleLatLonAltBoxChanged(GeoDataLatLonAltBox)), this, SLOT(setViewportChanged()));
638 connect(widget->model()->routingManager()->alternativeRoutesModel(),
639 SIGNAL(currentRouteChanged(const GeoDataDocument *)),
640 this,
641 SLOT(setViewportChanged()));
642 connect(widget->model()->routingManager()->alternativeRoutesModel(), SIGNAL(currentRouteChanged(const GeoDataDocument *)), this, SIGNAL(repaintNeeded()));
643 connect(widget->model()->routingManager()->alternativeRoutesModel(), SIGNAL(rowsInserted(QModelIndex, int, int)), this, SLOT(showAlternativeRoutes()));
644}
645
647{
648 delete d;
649}
650
652{
653 return QStringList(QStringLiteral("HOVERS_ABOVE_SURFACE"));
654}
655
657{
658 return 1.0;
659}
660
661bool RoutingLayer::render(GeoPainter *painter, ViewportParams *viewport, const QString &renderPos, GeoSceneLayer *layer)
662{
663 Q_UNUSED(viewport)
664 Q_UNUSED(renderPos)
665 Q_UNUSED(layer)
666
667 painter->save();
668
669 if (d->m_placemarkModel) {
670 d->renderPlacemarks(painter);
671 }
672
673 if (d->m_alternativeRoutesModel) {
674 d->renderAlternativeRoutes(painter);
675 }
676
677 d->renderRoute(painter);
678
679 if (d->m_routeRequest) {
680 d->renderRequest(painter);
681 }
682
683 d->renderAnnotations(painter);
684
685 painter->restore();
686 if (d->m_viewportChanged && d->m_viewContext == Still) {
687 d->m_viewportChanged = false;
688 }
689 return true;
690}
691
692RenderState RoutingLayer::renderState() const
693{
694 return RenderState(QStringLiteral("Routing"),
695 d->m_marbleWidget->model()->routingManager()->state() == RoutingManager::Downloading ? WaitingForUpdate : Complete);
696}
697
699{
700 Q_UNUSED(obj)
701
702 if (!d->m_isInteractive) {
703 return false;
704 }
705
706 if (event->type() == QEvent::MouseButtonPress) {
707 auto e = static_cast<QMouseEvent *>(event);
708 return d->handleMouseButtonPress(e);
709 }
710
711 if (event->type() == QEvent::MouseButtonRelease) {
712 auto e = static_cast<QMouseEvent *>(event);
713 return d->handleMouseButtonRelease(e);
714 }
715
716 if (event->type() == QEvent::MouseMove) {
717 auto e = static_cast<QMouseEvent *>(event);
718 return d->handleMouseMove(e);
719 }
720
721 return false;
722}
723
725{
726 d->m_placemarkModel = model;
727 setViewportChanged();
728}
729
731{
732 d->m_selectionModel = selection;
733}
734
735void RoutingLayer::removeViaPoint()
736{
737 if (d->m_activeMenuIndex >= 0) {
738 d->m_routeRequest->remove(d->m_activeMenuIndex);
739 d->m_activeMenuIndex = -1;
740 Q_EMIT repaintNeeded();
741 d->m_marbleWidget->model()->routingManager()->retrieveRoute();
742 }
743}
744
745void RoutingLayer::showAlternativeRoutes()
746{
747 setViewportChanged();
748 Q_EMIT repaintNeeded();
749}
750
751void RoutingLayer::exportRoute()
752{
753 QString fileName = QFileDialog::getSaveFileName(d->m_marbleWidget,
754 tr("Export Route"), // krazy:exclude=qclasses
756 tr("GPX and KML files (*.gpx *.kml)"));
757
758 if (!fileName.isEmpty()) {
759 if (fileName.endsWith(QLatin1StringView(".gpx"), Qt::CaseInsensitive)) {
760 QFile gpx(fileName);
761 if (gpx.open(QFile::WriteOnly)) {
762 d->m_routingModel->exportGpx(&gpx);
763 gpx.close();
764 }
765 } else {
766 d->m_marbleWidget->model()->routingManager()->saveRoute(fileName);
767 }
768 }
769}
770
771void RoutingLayer::updateRouteState()
772{
773 setViewportChanged();
774 Q_EMIT repaintNeeded();
775}
776
777void RoutingLayer::setViewportChanged()
778{
779 d->m_viewportChanged = true;
780 d->m_routeRegion = QRegion();
781 d->m_instructionRegions.clear();
782 d->m_alternativeRouteRegions.clear();
783}
784
786{
787 d->m_viewContext = viewContext;
788}
789
790void RoutingLayer::setInteractive(bool interactive)
791{
792 d->m_isInteractive = interactive;
793}
794
796{
797 return d->m_isInteractive;
798}
799
800QString RoutingLayer::runtimeTrace() const
801{
802 return QStringLiteral("Routing Layer");
803}
804
805} // namespace Marble
806
807#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:86
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=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.
@ 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
Q_EMITQ_EMIT
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)
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 Nov 8 2024 12:02:44 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.