6#include "RoutingWidget.h"
8#include "AlternativeRoutesModel.h"
9#include "CloudRoutesDialog.h"
10#include "CloudSyncManager.h"
11#include "GeoDataAnimatedUpdate.h"
12#include "GeoDataCreate.h"
13#include "GeoDataDelete.h"
14#include "GeoDataDocument.h"
15#include "GeoDataFlyTo.h"
16#include "GeoDataIconStyle.h"
17#include "GeoDataLineString.h"
18#include "GeoDataLookAt.h"
19#include "GeoDataPlacemark.h"
20#include "GeoDataPlaylist.h"
21#include "GeoDataStyle.h"
22#include "GeoDataTour.h"
23#include "GeoDataTreeModel.h"
24#include "GeoDataUpdate.h"
27#include "MarblePlacemarkModel.h"
29#include "MarbleWidgetInputHandler.h"
31#include "PlaybackAnimatedUpdateItem.h"
33#include "RouteRequest.h"
34#include "RouteSyncManager.h"
35#include "RoutingInputWidget.h"
36#include "RoutingLayer.h"
37#include "RoutingModel.h"
38#include "RoutingProfileSettingsDialog.h"
39#include "RoutingProfilesModel.h"
40#include "TourPlayback.h"
46#include <QProgressDialog>
51#include "ui_RoutingWidget.h"
59 GeoDataCoordinates coordinates;
63 WaypointInfo(
int index_,
double distance_,
const GeoDataCoordinates &coordinates_, Maneuver maneuver_,
const QString &info_)
66 , coordinates(coordinates_)
74class RoutingWidgetPrivate
77 Ui::RoutingWidget m_ui;
78 MarbleWidget *
const m_widget;
79 RoutingManager *
const m_routingManager;
80 RoutingLayer *
const m_routingLayer;
81 RoutingInputWidget *m_activeInput;
82 QList<RoutingInputWidget *> m_inputWidgets;
83 RoutingInputWidget *m_inputRequest;
84 QAbstractItemModel *
const m_routingModel;
85 RouteRequest *
const m_routeRequest;
86 RouteSyncManager *m_routeSyncManager;
87 bool m_zoomRouteAfterDownload;
88 QTimer m_progressTimer;
89 QList<QIcon> m_progressAnimation;
90 GeoDataDocument *m_document;
92 TourPlayback *m_playback;
99 QToolBar *m_toolBar =
nullptr;
101 QToolButton *m_openRouteButton =
nullptr;
102 QToolButton *m_saveRouteButton =
nullptr;
103 QAction *m_cloudSyncSeparator =
nullptr;
104 QAction *m_uploadToCloudAction =
nullptr;
105 QAction *m_openCloudRoutesAction =
nullptr;
106 QToolButton *m_addViaButton =
nullptr;
107 QToolButton *m_reverseRouteButton =
nullptr;
108 QToolButton *m_clearRouteButton =
nullptr;
109 QToolButton *m_configureButton =
nullptr;
110 QToolButton *m_playButton =
nullptr;
112 QProgressDialog *m_routeUploadDialog =
nullptr;
115 RoutingWidgetPrivate(RoutingWidget *parent, MarbleWidget *marbleWidget);
121 void adjustInputWidgets();
123 void adjustSearchButton();
131 void setActiveInput(RoutingInputWidget *widget);
136 void createProgressAnimation();
137 RoutingWidget *m_parent;
141 : m_widget(marbleWidget)
142 , m_routingManager(marbleWidget->model()->routingManager())
143 , m_routingLayer(marbleWidget->routingLayer())
144 , m_activeInput(nullptr)
145 , m_inputRequest(nullptr)
146 , m_routingModel(m_routingManager->routingModel())
147 , m_routeRequest(marbleWidget->model()->routingManager()->routeRequest())
148 , m_routeSyncManager(nullptr)
149 , m_zoomRouteAfterDownload(false)
150 , m_document(nullptr)
152 , m_playback(nullptr)
155 , m_collapse_width(0)
157 , m_planetId(marbleWidget->model()->planetId())
159 , m_openRouteButton(nullptr)
160 , m_saveRouteButton(nullptr)
161 , m_cloudSyncSeparator(nullptr)
162 , m_uploadToCloudAction(nullptr)
163 , m_openCloudRoutesAction(nullptr)
164 , m_addViaButton(nullptr)
165 , m_reverseRouteButton(nullptr)
166 , m_clearRouteButton(nullptr)
167 , m_configureButton(nullptr)
168 , m_routeUploadDialog(nullptr)
171 createProgressAnimation();
172 m_progressTimer.setInterval(100);
173 if (MarbleGlobal::getInstance()->profiles() & MarbleGlobal::SmallScreen) {
178void RoutingWidgetPrivate::adjustInputWidgets()
180 for (
int i = 0; i < m_inputWidgets.size(); ++i) {
181 m_inputWidgets[i]->setIndex(i);
184 adjustSearchButton();
187void RoutingWidgetPrivate::adjustSearchButton()
190 QString tooltip =
QObject::tr(
"Retrieve routing instructions for the selected destinations.");
193 for (
int i = 0; i < m_inputWidgets.size(); ++i) {
194 if (m_inputWidgets[i]->hasTargetPosition()) {
199 if (validInputs < 2) {
201 tooltip =
QObject::tr(
"Find places matching the search term");
204 m_ui.searchButton->setText(text);
205 m_ui.searchButton->setToolTip(tooltip);
208void RoutingWidgetPrivate::setActiveInput(RoutingInputWidget *widget)
210 Q_ASSERT(widget &&
"Must not pass null");
211 MarblePlacemarkModel *model = widget->searchResultModel();
213 m_activeInput = widget;
214 m_ui.directionsListView->setModel(model);
215 m_routingLayer->setPlacemarkModel(model);
216 m_routingLayer->synchronizeWith(m_ui.directionsListView->selectionModel());
219void RoutingWidgetPrivate::setupToolBar()
225 m_openRouteButton->setIcon(
QIcon(QStringLiteral(
":/icons/16x16/document-open.png")));
226 m_toolBar->addWidget(m_openRouteButton);
230 m_saveRouteButton->setIcon(
QIcon(QStringLiteral(
":/icons/16x16/document-save.png")));
231 m_toolBar->addWidget(m_saveRouteButton);
235 m_playButton->setIcon(
QIcon(QStringLiteral(
":/marble/playback-play.png")));
236 m_toolBar->addWidget(m_playButton);
238 m_cloudSyncSeparator = m_toolBar->addSeparator();
239 m_uploadToCloudAction = m_toolBar->addAction(
QObject::tr(
"Upload to Cloud"));
240 m_uploadToCloudAction->setToolTip(
QObject::tr(
"Upload to Cloud"));
241 m_uploadToCloudAction->setIcon(
QIcon(QStringLiteral(
":/icons/cloud-upload.png")));
243 m_openCloudRoutesAction = m_toolBar->addAction(
QObject::tr(
"Manage Cloud Routes"));
244 m_openCloudRoutesAction->setToolTip(
QObject::tr(
"Manage Cloud Routes"));
245 m_openCloudRoutesAction->setIcon(
QIcon(QStringLiteral(
":/icons/cloud-download.png")));
247 m_toolBar->addSeparator();
250 m_addViaButton->setIcon(
QIcon(QStringLiteral(
":/marble/list-add.png")));
251 m_toolBar->addWidget(m_addViaButton);
255 m_reverseRouteButton->setIcon(
QIcon(QStringLiteral(
":/marble/reverse.png")));
256 m_toolBar->addWidget(m_reverseRouteButton);
260 m_clearRouteButton->setIcon(
QIcon(QStringLiteral(
":/marble/edit-clear.png")));
261 m_toolBar->addWidget(m_clearRouteButton);
263 m_toolBar->addSeparator();
267 m_configureButton->setIcon(
QIcon(QStringLiteral(
":/icons/16x16/configure.png")));
268 m_toolBar->addWidget(m_configureButton);
270 QObject::connect(m_openRouteButton, SIGNAL(clicked()), m_parent, SLOT(openRoute()));
271 QObject::connect(m_saveRouteButton, SIGNAL(clicked()), m_parent, SLOT(saveRoute()));
272 QObject::connect(m_uploadToCloudAction, SIGNAL(triggered()), m_parent, SLOT(uploadToCloud()));
273 QObject::connect(m_openCloudRoutesAction, SIGNAL(triggered()), m_parent, SLOT(openCloudRoutesDialog()));
274 QObject::connect(m_addViaButton, SIGNAL(clicked()), m_parent, SLOT(addInputWidget()));
275 QObject::connect(m_reverseRouteButton, SIGNAL(clicked()), m_routingManager, SLOT(reverseRoute()));
276 QObject::connect(m_clearRouteButton, SIGNAL(clicked()), m_routingManager, SLOT(clearRoute()));
277 QObject::connect(m_configureButton, SIGNAL(clicked()), m_parent, SLOT(configureProfile()));
278 QObject::connect(m_playButton, SIGNAL(clicked()), m_parent, SLOT(toggleRoutePlay()));
280 m_toolBar->setIconSize(
QSize(16, 16));
284void RoutingWidgetPrivate::createProgressAnimation()
287 qreal
const h = m_iconSize / 2.0;
288 qreal
const q = h / 2.0;
290 qreal
const r = d / 2.0;
300 for (
double t = 0.0; t < 2 * M_PI; t += M_PI / 8.0) {
302 QRectF firstCircle(h - r + q * cos(t), h - r + q * sin(t), d, d);
303 QRectF secondCircle(h - r + q * cos(t + M_PI), h - r + q * sin(t + M_PI), d, d);
304 painter.drawEllipse(firstCircle);
305 painter.drawEllipse(secondCircle);
312 , d(new RoutingWidgetPrivate(this, marbleWidget))
314 d->m_ui.setupUi(
this);
316 d->m_ui.routeComboBox->setVisible(
false);
317 d->m_ui.routeComboBox->setModel(d->m_routingManager->alternativeRoutesModel());
320 d->m_ui.routingProfileComboBox->setModel(d->m_routingManager->profilesModel());
322 connect(d->m_routingManager->profilesModel(), SIGNAL(rowsInserted(
QModelIndex,
int,
int)),
this, SLOT(selectFirstProfile()));
323 connect(d->m_routingManager->profilesModel(), SIGNAL(modelReset()),
this, SLOT(selectFirstProfile()));
325 connect(d->m_routingManager, SIGNAL(stateChanged(RoutingManager::State)),
this, SLOT(updateRouteState(RoutingManager::State)));
326 connect(d->m_routeRequest, SIGNAL(positionAdded(
int)),
this, SLOT(insertInputWidget(
int)));
327 connect(d->m_routeRequest, SIGNAL(positionRemoved(
int)),
this, SLOT(removeInputWidget(
int)));
328 connect(d->m_routeRequest, SIGNAL(routingProfileChanged()),
this, SLOT(updateActiveRoutingProfile()));
329 connect(&d->m_progressTimer, SIGNAL(timeout()),
this, SLOT(updateProgress()));
330 connect(d->m_ui.routeComboBox, SIGNAL(currentIndexChanged(
int)), d->m_routingManager->alternativeRoutesModel(), SLOT(setCurrentRoute(
int)));
331 connect(d->m_routingManager->alternativeRoutesModel(), SIGNAL(currentRouteChanged(
int)), d->m_ui.routeComboBox, SLOT(setCurrentIndex(
int)));
332 connect(d->m_ui.routingProfileComboBox, SIGNAL(currentIndexChanged(
int)),
this, SLOT(setRoutingProfile(
int)));
333 connect(d->m_ui.routingProfileComboBox, SIGNAL(activated(
int)),
this, SLOT(retrieveRoute()));
334 connect(d->m_routingManager->alternativeRoutesModel(), SIGNAL(rowsInserted(
QModelIndex,
int,
int)),
this, SLOT(updateAlternativeRoutes()));
336 d->m_ui.directionsListView->setModel(d->m_routingModel);
339 d->m_routingLayer->synchronizeWith(selectionModel);
343 connect(d->m_ui.searchButton, SIGNAL(clicked()),
this, SLOT(retrieveRoute()));
344 connect(d->m_ui.showInstructionsButton, SIGNAL(clicked(
bool)),
this, SLOT(showDirections()));
346 for (
int i = 0; i < d->m_routeRequest->size(); ++i) {
347 insertInputWidget(i);
350 for (
int i = 0; i < 2 && d->m_inputWidgets.size() < 2; ++i) {
355 d->m_ui.resultLabel->setVisible(
false);
357 updateActiveRoutingProfile();
358 updateCloudSyncButtons();
360 if (MarbleGlobal::getInstance()->profiles() & MarbleGlobal::SmallScreen) {
361 d->m_ui.directionsListView->setVisible(
false);
362 d->m_openRouteButton->setVisible(
false);
363 d->m_saveRouteButton->setVisible(
false);
366 connect(marbleWidget->
model(), SIGNAL(themeChanged(
QString)),
this, SLOT(handlePlanetChange()));
371 delete d->m_playback;
374 d->m_widget->model()->treeModel()->removeDocument(d->m_document);
375 delete d->m_document;
380void RoutingWidget::retrieveRoute()
382 if (d->m_inputWidgets.size() == 1) {
384 d->m_inputWidgets.first()->findPlacemarks();
388 int index = d->m_ui.routingProfileComboBox->currentIndex();
392 d->m_routeRequest->setRoutingProfile(d->m_routingManager->profilesModel()->profiles().at(index));
394 Q_ASSERT(d->m_routeRequest->size() == d->m_inputWidgets.size());
395 for (
int i = 0; i < d->m_inputWidgets.size(); ++i) {
396 RoutingInputWidget *widget = d->m_inputWidgets.at(i);
397 if (!widget->hasTargetPosition() && widget->hasInput()) {
398 widget->findPlacemarks();
403 d->m_activeInput =
nullptr;
404 if (d->m_routeRequest->size() > 1) {
405 d->m_zoomRouteAfterDownload =
true;
406 d->m_routingLayer->setPlacemarkModel(
nullptr);
407 d->m_routingManager->retrieveRoute();
408 d->m_ui.directionsListView->setModel(d->m_routingModel);
409 d->m_routingLayer->synchronizeWith(d->m_ui.directionsListView->selectionModel());
413 d->m_playback->stop();
417void RoutingWidget::activateItem(
const QModelIndex &index)
422 auto position = qvariant_cast<GeoDataCoordinates>(data);
423 d->m_widget->centerOn(position,
true);
426 if (d->m_activeInput && index.
isValid()) {
429 d->m_activeInput->setTargetPosition(data.
value<GeoDataCoordinates>(), index.
data().
toString());
436 d->setActiveInput(widget);
437 MarblePlacemarkModel *model = widget->searchResultModel();
439 if (model->rowCount()) {
440 QString
const results =
tr(
"placemarks found: %1").
arg(model->rowCount());
441 d->m_ui.resultLabel->setText(results);
442 d->m_ui.resultLabel->setVisible(
true);
444 activatePlacemark(model->index(0, 0));
446 QString
const results =
tr(
"No placemark found");
447 d->m_ui.resultLabel->setText(QLatin1StringView(
"<font color=\"red\">") + results + QLatin1StringView(
"</font>"));
448 d->m_ui.resultLabel->setVisible(
true);
451 GeoDataLineString placemarks;
452 for (
int i = 0; i < model->rowCount(); ++i) {
455 placemarks << data.
value<GeoDataCoordinates>();
459 if (placemarks.size() > 1) {
463 if (MarbleGlobal::getInstance()->profiles() & MarbleGlobal::SmallScreen) {
464 d->m_ui.directionsListView->setVisible(
true);
471 if (widget->hasTargetPosition()) {
472 d->m_widget->centerOn(widget->targetPosition());
476void RoutingWidget::activatePlacemark(
const QModelIndex &index)
478 if (d->m_activeInput && index.
isValid()) {
481 d->m_activeInput->setTargetPosition(data.
value<GeoDataCoordinates>());
485 d->m_ui.directionsListView->setCurrentIndex(index);
493void RoutingWidget::insertInputWidget(
int index)
495 if (index >= 0 && index <= d->m_inputWidgets.size()) {
497 d->m_inputWidgets.insert(index, input);
502 connect(input, SIGNAL(targetValidityChanged(
bool)),
this, SLOT(adjustSearchButton()));
504 d->m_ui.inputLayout->insertWidget(index, input);
505 d->adjustInputWidgets();
511 int index = d->m_inputWidgets.indexOf(widget);
513 if (d->m_inputWidgets.size() < 3) {
516 d->m_routeRequest->remove(index);
518 d->m_routingManager->retrieveRoute();
522void RoutingWidget::removeInputWidget(
int index)
524 if (index >= 0 && index < d->m_inputWidgets.size()) {
525 RoutingInputWidget *widget = d->m_inputWidgets.at(index);
526 d->m_inputWidgets.remove(index);
527 d->m_ui.inputLayout->removeWidget(widget);
528 widget->deleteLater();
529 if (widget == d->m_activeInput) {
530 d->m_activeInput =
nullptr;
531 d->m_routingLayer->setPlacemarkModel(
nullptr);
532 d->m_ui.directionsListView->setModel(d->m_routingModel);
533 d->m_routingLayer->synchronizeWith(d->m_ui.directionsListView->selectionModel());
535 d->adjustInputWidgets();
538 if (d->m_inputWidgets.size() < 2) {
543void RoutingWidget::updateRouteState(RoutingManager::State state)
548 case RoutingManager::Downloading:
549 d->m_ui.routeComboBox->setVisible(
false);
550 d->m_ui.routeComboBox->clear();
551 d->m_progressTimer.start();
552 d->m_ui.resultLabel->setVisible(
false);
554 case RoutingManager::Retrieved: {
555 d->m_progressTimer.stop();
556 d->m_ui.searchButton->setIcon(QIcon());
557 if (d->m_routingManager->routingModel()->rowCount() == 0) {
558 const QString results =
tr(
"No route found");
559 d->m_ui.resultLabel->setText(QLatin1StringView(
"<font color=\"red\">") + results + QLatin1StringView(
"</font>"));
560 d->m_ui.resultLabel->setVisible(
true);
565 d->m_saveRouteButton->setEnabled(d->m_routingManager->routingModel()->rowCount() > 0);
570 pointSelectionCanceled();
573 d->m_inputRequest = widget;
574 d->m_widget->installEventFilter(
this);
581 if (d->m_inputRequest && d->m_inputWidgets.contains(d->m_inputRequest)) {
582 d->m_inputRequest->setTargetPosition(coordinates);
583 d->m_widget->update();
586 d->m_inputRequest =
nullptr;
587 d->m_widget->removeEventFilter(
this);
590void RoutingWidget::adjustSearchButton()
592 d->adjustSearchButton();
595void RoutingWidget::pointSelectionCanceled()
597 if (d->m_inputRequest && d->m_inputWidgets.contains(d->m_inputRequest)) {
598 d->m_inputRequest->abortMapInputRequest();
601 d->m_inputRequest =
nullptr;
602 d->m_widget->removeEventFilter(
this);
605void RoutingWidget::configureProfile()
607 int index = d->m_ui.routingProfileComboBox->currentIndex();
609 RoutingProfileSettingsDialog dialog(d->m_widget->model()->pluginManager(), d->m_routingManager->profilesModel(),
this);
610 dialog.editProfile(d->m_ui.routingProfileComboBox->currentIndex());
611 d->m_routeRequest->setRoutingProfile(d->m_routingManager->profilesModel()->profiles().at(index));
615void RoutingWidget::updateProgress()
617 if (!d->m_progressAnimation.isEmpty()) {
618 d->m_currentFrame = (d->m_currentFrame + 1) % d->m_progressAnimation.size();
619 QIcon frame = d->m_progressAnimation[d->m_currentFrame];
620 d->m_ui.searchButton->setIcon(frame);
624void RoutingWidget::updateAlternativeRoutes()
626 if (d->m_ui.routeComboBox->count() == 1) {
628 GeoDataLatLonBox
const bbox = d->m_routingManager->routingModel()->route().bounds();
629 if (d->m_zoomRouteAfterDownload) {
630 d->m_zoomRouteAfterDownload =
false;
631 d->m_widget->centerOn(bbox);
635 d->m_ui.routeComboBox->setVisible(d->m_ui.routeComboBox->count() > 0);
636 if (d->m_ui.routeComboBox->currentIndex() < 0 && d->m_ui.routeComboBox->count() > 0) {
637 d->m_ui.routeComboBox->setCurrentIndex(0);
640 QString
const results =
tr(
"routes found: %1").
arg(d->m_ui.routeComboBox->count());
641 d->m_ui.resultLabel->setText(results);
642 d->m_ui.resultLabel->setVisible(
true);
643 d->m_saveRouteButton->setEnabled(d->m_routingManager->routingModel()->rowCount() > 0);
648 d->m_ui.showInstructionsButton->setVisible(
visible);
651void RoutingWidget::setRouteSyncManager(RouteSyncManager *manager)
653 d->m_routeSyncManager = manager;
654 connect(d->m_routeSyncManager, SIGNAL(routeSyncEnabledChanged(
bool)),
this, SLOT(updateCloudSyncButtons()));
655 updateCloudSyncButtons();
662 d->m_routingManager->setLastOpenPath(
QFileInfo(file).absolutePath());
663 d->m_zoomRouteAfterDownload =
true;
664 d->m_routingManager->loadRoute(file);
665 updateAlternativeRoutes();
669void RoutingWidget::selectFirstProfile()
671 int count = d->m_routingManager->profilesModel()->rowCount();
672 if (count && d->m_ui.routingProfileComboBox->currentIndex() < 0) {
673 d->m_ui.routingProfileComboBox->setCurrentIndex(0);
677void RoutingWidget::setRoutingProfile(
int index)
679 if (index >= 0 && index < d->m_routingManager->profilesModel()->rowCount()) {
680 d->m_routeRequest->setRoutingProfile(d->m_routingManager->profilesModel()->profiles().at(index));
684void RoutingWidget::showDirections()
686 d->m_ui.directionsListView->setVisible(
true);
693 d->m_routingManager->lastSavePath(),
694 tr(
"KML files (*.kml)"));
701 d->m_routingManager->setLastSavePath(
QFileInfo(fileName).absolutePath());
702 d->m_routingManager->saveRoute(fileName);
708 Q_ASSERT(d->m_routeSyncManager);
710 if (!d->m_routeUploadDialog) {
712 d->m_routeUploadDialog->setWindowTitle(
tr(
"Uploading route..."));
713 d->m_routeUploadDialog->setMinimum(0);
714 d->m_routeUploadDialog->setMaximum(100);
715 d->m_routeUploadDialog->setAutoClose(
true);
716 d->m_routeUploadDialog->setAutoReset(
true);
717 connect(d->m_routeSyncManager, SIGNAL(routeUploadProgress(qint64, qint64)),
this, SLOT(updateUploadProgress(qint64, qint64)));
720 d->m_routeUploadDialog->show();
721 d->m_routeSyncManager->uploadRoute();
726 Q_ASSERT(d->m_routeSyncManager);
727 d->m_routeSyncManager->prepareRouteList();
730 connect(d->m_routeSyncManager, SIGNAL(routeListDownloadProgress(qint64, qint64)), dialog, SLOT(updateListDownloadProgressbar(qint64, qint64)));
731 connect(dialog, SIGNAL(downloadButtonClicked(
QString)), d->m_routeSyncManager, SLOT(downloadRoute(
QString)));
733 connect(dialog, SIGNAL(deleteButtonClicked(
QString)), d->m_routeSyncManager, SLOT(deleteRoute(
QString)));
734 connect(dialog, SIGNAL(removeFromCacheButtonClicked(
QString)), d->m_routeSyncManager, SLOT(removeRouteFromCache(
QString)));
735 connect(dialog, SIGNAL(uploadToCloudButtonClicked(
QString)), d->m_routeSyncManager, SLOT(uploadRoute(
QString)));
740void RoutingWidget::updateActiveRoutingProfile()
742 RoutingProfile
const profile = d->m_routingManager->routeRequest()->routingProfile();
744 d->m_ui.routingProfileComboBox->setCurrentIndex(profiles.
indexOf(profile));
747void RoutingWidget::updateCloudSyncButtons()
749 bool const show = d->m_routeSyncManager && d->m_routeSyncManager->isRouteSyncEnabled();
750 d->m_cloudSyncSeparator->setVisible(
show);
751 d->m_uploadToCloudAction->setVisible(
show);
752 d->m_openCloudRoutesAction->setVisible(
show);
755void RoutingWidget::openCloudRoute(
const QString &identifier)
757 Q_ASSERT(d->m_routeSyncManager);
758 d->m_routeSyncManager->openRoute(identifier);
759 d->m_widget->centerOn(d->m_routingManager->routingModel()->route().bounds());
762void RoutingWidget::updateUploadProgress(qint64 sent, qint64 total)
764 Q_ASSERT(d->m_routeUploadDialog);
765 d->m_routeUploadDialog->setValue(100.0 * sent / total);
768bool RoutingWidget::eventFilter(QObject *o, QEvent *event)
770 if (o != d->m_widget) {
774 Q_ASSERT(d->m_inputRequest !=
nullptr);
775 Q_ASSERT(d->m_inputWidgets.contains(d->m_inputRequest));
778 auto e =
static_cast<QMouseEvent *
>(
event);
783 auto e =
static_cast<QMouseEvent *
>(
event);
784 qreal lon(0.0), lat(0.0);
785 if (e->button() ==
Qt::LeftButton && d->m_widget->geoCoordinates(e->pos().x(), e->pos().y(), lon, lat, GeoDataCoordinates::Radian)) {
786 retrieveSelectedPoint(GeoDataCoordinates(lon, lat));
799 auto e =
static_cast<QKeyEvent *
>(
event);
801 pointSelectionCanceled();
811void RoutingWidget::resizeEvent(QResizeEvent *e)
816void RoutingWidget::toggleRoutePlay()
818 if (!d->m_playback) {
819 if (d->m_routingModel->rowCount() != 0) {
829 d->m_playButton->setIcon(QIcon(QStringLiteral(
":/marble/playback-pause.png")));
832 d->m_playback->play();
835 d->m_playing =
false;
836 d->m_playButton->setIcon(QIcon(QStringLiteral(
":/marble/playback-play.png")));
837 d->m_playback->pause();
841void RoutingWidget::initializeTour()
843 d->m_tour =
new GeoDataTour;
845 d->m_widget->model()->treeModel()->removeDocument(d->m_document);
846 delete d->m_document;
848 d->m_document =
new GeoDataDocument;
849 d->m_document->setId(QStringLiteral(
"tourdoc"));
850 d->m_document->append(d->m_tour);
852 d->m_tour->setPlaylist(
new GeoDataPlaylist);
853 Route
const route = d->m_widget->model()->routingManager()->routingModel()->route();
854 GeoDataLineString
path = route.path();
859 QList<WaypointInfo> waypoints;
860 double totalDistance = 0.0;
861 for (
int i = 0; i < route.size(); ++i) {
863 waypoints << WaypointInfo(i, totalDistance, route.at(i).path().first(), route.at(i).maneuver(), QLatin1StringView(
"start ") +
QString::number(i));
864 totalDistance += route.at(i).distance();
867 if (waypoints.
size() < 1) {
871 QList<WaypointInfo>
const allWaypoints = waypoints;
873 GeoDataCoordinates last =
path.
at(0);
875 qreal planetRadius = d->m_widget->model()->planet()->radius();
876 for (
int i = 1; i <
path.
size(); ++i) {
877 GeoDataCoordinates coordinates =
path.
at(i);
878 totalDistance += planetRadius *
path.
at(i - 1).sphericalDistanceTo(coordinates);
879 while (totalDistance >= allWaypoints[j].distance && j + 1 < allWaypoints.
size()) {
882 int const lastIndex = qBound(0, j - 1, allWaypoints.
size() - 1);
883 double const lastDistance = qAbs(totalDistance - allWaypoints[lastIndex].distance);
884 double const nextDistance = qAbs(allWaypoints[j].distance - totalDistance);
885 double const waypointDistance = qMin(lastDistance, nextDistance);
886 double const step = qBound(100.0, waypointDistance * 2, 1000.0);
889 if (i > 1 && distance < step) {
894 auto lookat =
new GeoDataLookAt;
896 double const range = waypointDistance < 400 ? 400 : (waypointDistance < 2000 ? 600 : 800);
898 lookat->setCoordinates(coordinates);
899 lookat->setRange(range);
900 auto flyto =
new GeoDataFlyTo;
901 double const duration = 0.75;
902 flyto->setDuration(duration);
903 flyto->setView(lookat);
904 flyto->setFlyToMode(GeoDataFlyTo::Smooth);
905 d->m_tour->playlist()->addPrimitive(flyto);
907 if (!waypoints.
empty() && totalDistance > waypoints.
first().distance - 100) {
908 WaypointInfo
const waypoint = waypoints.
first();
910 auto updateCreate =
new GeoDataAnimatedUpdate;
911 updateCreate->setUpdate(
new GeoDataUpdate);
912 updateCreate->update()->setCreate(
new GeoDataCreate);
913 auto placemarkCreate =
new GeoDataPlacemark;
914 QString
const waypointId = QStringLiteral(
"waypoint-%1").arg(i, 0, 10);
915 placemarkCreate->setId(waypointId);
916 placemarkCreate->setTargetId(d->m_document->id());
917 placemarkCreate->setCoordinate(waypoint.coordinates);
918 GeoDataStyle::Ptr
style(
new GeoDataStyle);
919 style->iconStyle().setIconPath(waypoint.maneuver.directionPixmap());
920 placemarkCreate->setStyle(
style);
921 updateCreate->update()->create()->append(placemarkCreate);
922 d->m_tour->playlist()->addPrimitive(updateCreate);
924 auto updateDelete =
new GeoDataAnimatedUpdate;
925 updateDelete->setDelayedStart(2);
926 updateDelete->setUpdate(
new GeoDataUpdate);
927 updateDelete->update()->setDelete(
new GeoDataDelete);
928 auto placemarkDelete =
new GeoDataPlacemark;
929 placemarkDelete->setTargetId(waypointId);
930 updateDelete->update()->getDelete()->append(placemarkDelete);
931 d->m_tour->playlist()->addPrimitive(updateDelete);
935 d->m_playback =
new TourPlayback;
936 d->m_playback->setMarbleWidget(d->m_widget);
937 d->m_playback->setTour(d->m_tour);
938 d->m_widget->model()->treeModel()->addDocument(d->m_document);
939 QObject::connect(d->m_playback, SIGNAL(finished()),
this, SLOT(seekTourToStart()));
945 GeoDataLookAt lookat;
946 lookat.setCoordinates(coordinates);
947 lookat.setRange(coordinates.
altitude());
948 d->m_widget->flyTo(lookat,
Instant);
952void RoutingWidget::clearTour()
954 d->m_playing =
false;
955 d->m_playButton->setIcon(QIcon(QStringLiteral(
":/marble/playback-play.png")));
956 delete d->m_playback;
957 d->m_playback =
nullptr;
959 d->m_widget->model()->treeModel()->removeDocument(d->m_document);
960 delete d->m_document;
961 d->m_document =
nullptr;
966void RoutingWidget::seekTourToStart()
968 Q_ASSERT(d->m_playback);
969 d->m_playback->stop();
970 d->m_playback->seek(0);
971 d->m_playButton->setIcon(QIcon(QStringLiteral(
":/marble/playback-play.png")));
972 d->m_playing =
false;
975void RoutingWidget::handlePlanetChange()
977 const QString newPlanetId = d->m_widget->model()->planetId();
979 if (newPlanetId == d->m_planetId) {
983 d->m_planetId = newPlanetId;
984 d->m_routingManager->clearRoute();
989#include "moc_RoutingWidget.cpp"
This file contains the headers for MarbleModel.
void setAltitude(const qreal altitude)
set the altitude of the Point in meters
qreal altitude() const
return the altitude of the Point in meters
qreal sphericalDistanceTo(const GeoDataCoordinates &other) const
This method calculates the shortest distance between two points on a sphere.
A 3d point representation.
static GeoDataLatLonBox fromLineString(const GeoDataLineString &lineString)
Create the smallest bounding box from a line string.
@ CoordinateRole
The GeoDataCoordinates coordinate.
QString path(const QString &relativePath)
Binds a QML item to a specific geodetic location in screen coordinates.
@ Instant
Change camera position immediately (no interpolation)
KOSM_EXPORT double distance(const std::vector< const OSM::Node * > &path, Coordinate coord)
QString getOpenFileName(QWidget *parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, Options options)
QString getSaveFileName(QWidget *parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, Options options)
qsizetype indexOf(const AT &value, qsizetype from) const const
qsizetype size() 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)
virtual bool eventFilter(QObject *watched, QEvent *event)
QObject * parent() const const
QString tr(const char *sourceText, const char *disambiguation, int n)
QPixmap fromImage(QImage &&image, Qt::ImageConversionFlags flags)
QString arg(Args &&... args) const const
const QChar at(qsizetype position) const const
bool endsWith(QChar c, Qt::CaseSensitivity cs) const const
bool isEmpty() const const
QString number(double n, char format, int precision)
qsizetype size() const const
bool isNull() const const
QString toString() const const
void setContentsMargins(const QMargins &margins)