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;
83 RoutingInputWidget *m_inputRequest;
85 RouteRequest *
const m_routeRequest;
86 RouteSyncManager *m_routeSyncManager;
87 bool m_zoomRouteAfterDownload;
90 GeoDataDocument *m_document;
92 TourPlayback *m_playback;
103 QAction *m_cloudSyncSeparator =
nullptr;
104 QAction *m_uploadToCloudAction =
nullptr;
105 QAction *m_openCloudRoutesAction =
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;
140RoutingWidgetPrivate::RoutingWidgetPrivate(RoutingWidget *parent, MarbleWidget *marbleWidget)
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());
434void RoutingWidget::handleSearchResult(RoutingInputWidget *widget)
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");
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);
469void RoutingWidget::centerOnInputWidget(RoutingInputWidget *widget)
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();
509void RoutingWidget::removeInputWidget(RoutingInputWidget *widget)
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");
560 d->m_ui.resultLabel->setVisible(
true);
565 d->m_saveRouteButton->setEnabled(d->m_routingManager->routingModel()->rowCount() > 0);
568void RoutingWidget::requestMapPosition(RoutingInputWidget *widget,
bool enabled)
570 pointSelectionCanceled();
573 d->m_inputRequest = widget;
574 d->m_widget->installEventFilter(
this);
579void RoutingWidget::retrieveSelectedPoint(
const GeoDataCoordinates &coordinates)
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);
770 if (o != d->m_widget) {
774 Q_ASSERT(d->m_inputRequest !=
nullptr);
775 Q_ASSERT(d->m_inputWidgets.contains(d->m_inputRequest));
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));
801 pointSelectionCanceled();
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();
860 double totalDistance = 0.0;
861 for (
int i = 0; i < route.size(); ++i) {
864 totalDistance += route.at(i).distance();
867 if (waypoints.
size() < 1) {
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);
888 double const distance = planetRadius * last.sphericalDistanceTo(coordinates);
889 if (i > 1 && distance < step) {
894 auto lookat =
new GeoDataLookAt;
896 double const range = waypointDistance < 400 ? 400 : (waypointDistance < 2000 ? 600 : 800);
897 coordinates.setAltitude(range);
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()));
942void RoutingWidget::centerOn(
const GeoDataCoordinates &coordinates)
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.
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 eventFilter(QObject *watched, QEvent *event)
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)