diff --git a/src/lib/marble/BookmarkManagerDialog.cpp b/src/lib/marble/BookmarkManagerDialog.cpp index 0121b2c28..0c898f7bf 100644 --- a/src/lib/marble/BookmarkManagerDialog.cpp +++ b/src/lib/marble/BookmarkManagerDialog.cpp @@ -1,537 +1,537 @@ // // This file is part of the Marble Virtual Globe. // // This program is free software licensed under the GNU LGPL. You can // find a copy of this license in LICENSE.txt in the top directory of // the source code. // // Copyright 2010 Dennis Nienhüser // Copyright 2012 Thibaut Gridel // #include "BookmarkManagerDialog.h" #include "BookmarkManager.h" #include "BookmarkManager_p.h" #include "BranchFilterProxyModel.h" #include "EditBookmarkDialog.h" #include "FileManager.h" #include "GeoDataCoordinates.h" #include "GeoDataDocument.h" #include "GeoDataLookAt.h" #include "GeoDataExtendedData.h" #include "GeoDataFolder.h" #include "GeoDataPlacemark.h" #include "GeoDataPoint.h" #include "GeoDataStyle.h" #include "GeoDataIconStyle.h" #include "GeoDataTreeModel.h" #include "GeoDataTypes.h" #include "GeoDataDocumentWriter.h" #include "MarbleDirs.h" #include "MarbleDebug.h" #include "MarbleModel.h" #include "NewBookmarkFolderDialog.h" #include "MarblePlacemarkModel.h" #include #include #include #include #include #include namespace Marble { /* * The two list views use the model data like this: * * (folder filter) * * QSortFilterProxyModel => Folders View * / | * bookmarks.kml => GeoDataTreeModel | current folder sets filter * \ \ / * BranchFilterModel => Bookmarks View * * (placemark filter) (placemark list) * */ class BookmarkManagerDialogPrivate { Q_DECLARE_TR_FUNCTIONS(BookmarkManagerDialogPrivate) public: BookmarkManagerDialog *m_parent; BookmarkManager *const m_manager; GeoDataTreeModel *const m_treeModel; QSortFilterProxyModel m_folderFilterModel; QPersistentModelIndex m_selectedFolder; BranchFilterProxyModel m_branchFilterModel; BookmarkManagerDialogPrivate( BookmarkManagerDialog* parent, MarbleModel *model ); void initializeFoldersView( GeoDataTreeModel* treeModel ); void initializeBookmarksView( GeoDataTreeModel* treeModel ); void handleFolderSelection( const QModelIndex &index ); void updateButtonState(); void addNewFolder(); void renameFolder(); void deleteFolder(); void editBookmark(); void deleteBookmark(); void discardChanges(); QModelIndex bookmarkTreeIndex( const QModelIndex &bookmark ) const; QModelIndex folderTreeIndex( const QModelIndex &index ) const; GeoDataContainer* selectedFolder(); void selectFolder( const QString &name = QString(), const QModelIndex &index = QModelIndex() ); void importBookmarksRecursively( GeoDataContainer *source, GeoDataContainer *destination, bool &replaceAll, bool &skipAll ); GeoDataDocument* bookmarkDocument(); }; BookmarkManagerDialogPrivate::BookmarkManagerDialogPrivate( BookmarkManagerDialog* parent, MarbleModel *model ) : m_parent( parent ), m_manager( model->bookmarkManager() ), m_treeModel( model->treeModel() ), m_folderFilterModel(), m_branchFilterModel() { // nothing to do } /// react to clicking on the folder index (of folderfiltermodel fame) /// consequence is selecting this folder, or unselecting it and going to root folder void BookmarkManagerDialogPrivate::handleFolderSelection( const QModelIndex &index ) { if( !index.isValid() ) { return; } Q_ASSERT( index.isValid() ); Q_ASSERT( index.model() == &m_folderFilterModel ); if( m_selectedFolder.isValid() && m_parent->foldersTreeView->selectionModel()->selectedIndexes().contains( m_selectedFolder ) ) { m_selectedFolder = QModelIndex(); m_parent->foldersTreeView->selectionModel()->clear(); selectFolder(); } else { m_selectedFolder = index; m_branchFilterModel.setBranchIndex( m_treeModel, folderTreeIndex( index ) ); m_parent->bookmarksListView->setRootIndex( m_branchFilterModel.mapFromSource( folderTreeIndex( index ) ) ); m_parent->bookmarksListView->selectionModel()->clear(); } } void BookmarkManagerDialogPrivate::updateButtonState() { bool const hasFolderSelection = !m_parent->foldersTreeView->selectionModel()->selectedIndexes().isEmpty(); m_parent->renameFolderButton->setEnabled( hasFolderSelection ); m_parent->removeFolderButton->setEnabled( hasFolderSelection ); bool const hasBookmarkSelection = !m_parent->bookmarksListView->selectionModel()->selectedIndexes().isEmpty(); m_parent->editBookmarkButton->setEnabled( hasBookmarkSelection ); m_parent->removeBookmarkButton->setEnabled( hasBookmarkSelection ); } void BookmarkManagerDialogPrivate::addNewFolder() { QPointer dialog = new NewBookmarkFolderDialog( m_parent ); if ( dialog->exec() == QDialog::Accepted && !dialog->folderName().isEmpty() ) { m_manager->addNewBookmarkFolder( selectedFolder(), dialog->folderName() ); selectFolder( dialog->folderName(), m_selectedFolder ); } delete dialog; } void BookmarkManagerDialogPrivate::renameFolder() { GeoDataFolder *folder = dynamic_cast(selectedFolder()); if ( folder ) { QPointer dialog = new NewBookmarkFolderDialog( m_parent ); dialog->setFolderName( folder->name() ); QPersistentModelIndex parentIndex = m_selectedFolder.parent(); if ( dialog->exec() == QDialog::Accepted ) { m_manager->renameBookmarkFolder( folder, dialog->folderName() ); } selectFolder( dialog->folderName(), parentIndex ); delete dialog; } } void BookmarkManagerDialogPrivate::deleteFolder() { GeoDataFolder *folder = dynamic_cast(selectedFolder()); if ( folder ) { if ( folder->size() > 0 ) { QString const text = tr( "The folder %1 is not empty. Removing it will delete all bookmarks it contains. Are you sure you want to delete the folder?" ).arg( folder->name() ); if (QMessageBox::question(m_parent, tr("Remove Folder"), text, QMessageBox::Yes, QMessageBox::No) != QMessageBox::Yes) { return; } } // take note of the parentIndex before removing the folder QString parent = static_cast(folder->parent())->name(); QPersistentModelIndex greatParentIndex = m_selectedFolder.parent().parent(); m_manager->removeBookmarkFolder( folder ); selectFolder( parent, greatParentIndex); } } void BookmarkManagerDialogPrivate::editBookmark() { QModelIndexList selection = m_parent->bookmarksListView->selectionModel()->selectedIndexes(); if ( selection.size() == 1 ) { QModelIndex index = m_branchFilterModel.mapToSource( selection.first() ); Q_ASSERT( index.isValid() ); GeoDataObject* object = qvariant_cast( index.data( MarblePlacemarkModel::ObjectPointerRole ) ); Q_ASSERT( object ); GeoDataPlacemark* bookmark = dynamic_cast( object ); // do not try to edit folders if ( !bookmark ) { return; } Q_ASSERT( bookmark ); QModelIndex treeIndex = index; Q_ASSERT( treeIndex.isValid() ); QModelIndex folderIndex = treeIndex.parent(); Q_ASSERT( folderIndex.isValid() ); GeoDataObject* folderObject = qvariant_cast( folderIndex.data( MarblePlacemarkModel::ObjectPointerRole ) ); Q_ASSERT( folderObject ); GeoDataFolder* folder = dynamic_cast( folderObject ); Q_ASSERT( folder ); QPointer dialog = new EditBookmarkDialog( m_manager, m_parent ); dialog->setName( bookmark->name() ); if ( bookmark->lookAt() ) { dialog->setRange( bookmark->lookAt()->range() ); } dialog->setCoordinates( bookmark->coordinate() ); dialog->setDescription( bookmark->description() ); dialog->setFolderName( folder->name() ); dialog->setIconLink( bookmark->style()->iconStyle().iconPath() ); if ( dialog->exec() == QDialog::Accepted ) { bookmark->setName( dialog->name() ); bookmark->setDescription( dialog->description() ); bookmark->setCoordinate( dialog->coordinates() ); GeoDataStyle::Ptr newStyle(new GeoDataStyle( *bookmark->style() )); newStyle->iconStyle().setIconPath( dialog->iconLink() ); bookmark->setStyle( newStyle ); if ( bookmark->lookAt() ) { bookmark->lookAt()->setCoordinates( dialog->coordinates() ); bookmark->lookAt()->setRange( dialog->range() ); } else if ( dialog->range() ) { GeoDataLookAt *lookat = new GeoDataLookAt; lookat->setCoordinates( dialog->coordinates() ); lookat->setRange( dialog->range() ); bookmark->setAbstractView( lookat ); } m_manager->updateBookmark( bookmark ); if (folder->name() != dialog->folder()->name() ) { GeoDataPlacemark newBookmark( *bookmark ); m_manager->removeBookmark( bookmark ); m_manager->addBookmark( dialog->folder(), newBookmark ); } } delete dialog; } } void BookmarkManagerDialogPrivate::deleteBookmark() { QModelIndexList selection = m_parent->bookmarksListView->selectionModel()->selectedIndexes(); if ( selection.size() == 1 ) { QModelIndex bookmarkIndex = m_branchFilterModel.mapToSource( selection.first() ); GeoDataFolder* folder = dynamic_cast( selectedFolder() ); if ( folder ) { GeoDataPlacemark* bookmark = dynamic_cast( folder->child( bookmarkIndex.row() ) ); if ( bookmark ) { m_manager->removeBookmark( bookmark ); } } } } void BookmarkManagerDialogPrivate::discardChanges() { m_manager->loadFile( "bookmarks/bookmarks.kml" ); } /// selects the folder name from its parent (of folder filter fame) void BookmarkManagerDialogPrivate::selectFolder( const QString &name, const QModelIndex &parent ) { if ( parent.isValid() ) { Q_ASSERT( parent.model() == &m_folderFilterModel ); } if ( name.isEmpty() ) { QModelIndex documentTreeIndex = m_treeModel->index( bookmarkDocument() ); QModelIndex folderFilterIndex = m_folderFilterModel.mapFromSource( documentTreeIndex ); Q_ASSERT( folderFilterIndex.isValid() ); m_parent->foldersTreeView->setCurrentIndex( folderFilterIndex ); handleFolderSelection( folderFilterIndex ); return; } for ( int i=0; i < m_folderFilterModel.rowCount( parent ); ++i ) { QModelIndex childIndex = m_folderFilterModel.index( i, 0, parent ); if ( childIndex.data().toString() == name && m_selectedFolder != childIndex ) { m_parent->foldersTreeView->setCurrentIndex( childIndex ); handleFolderSelection( childIndex ); return; } if ( m_folderFilterModel.hasChildren( childIndex ) ) { selectFolder( name, childIndex ); } } } QModelIndex BookmarkManagerDialogPrivate::folderTreeIndex( const QModelIndex &index ) const { Q_ASSERT( index.isValid() ); Q_ASSERT( index.model() == &m_folderFilterModel ); QModelIndex const treeModelIndex = m_folderFilterModel.mapToSource( index ); Q_ASSERT( treeModelIndex.isValid() ); Q_ASSERT( treeModelIndex.model() == m_treeModel ); return treeModelIndex; } GeoDataContainer *BookmarkManagerDialogPrivate::selectedFolder() { if( m_selectedFolder.isValid() ) { GeoDataObject* object = qvariant_cast( m_selectedFolder.data( MarblePlacemarkModel::ObjectPointerRole ) ); Q_ASSERT( object ); GeoDataContainer* container = dynamic_cast( object ); Q_ASSERT( container ); return container; } else { return bookmarkDocument(); } } void BookmarkManagerDialogPrivate::initializeFoldersView( GeoDataTreeModel* treeModel ) { m_folderFilterModel.setFilterKeyColumn( 1 ); const QString regexp = QLatin1String(GeoDataTypes::GeoDataFolderType) + QLatin1Char('|') + QLatin1String(GeoDataTypes::GeoDataDocumentType); m_folderFilterModel.setFilterRegExp( regexp ); m_folderFilterModel.setSourceModel( treeModel ); m_parent->foldersTreeView->setModel( &m_folderFilterModel ); m_parent->foldersTreeView->setEditTriggers( QAbstractItemView::NoEditTriggers ); m_parent->foldersTreeView->setHeaderHidden( true ); for ( int i=1; icolumnCount(); ++i ) { m_parent->foldersTreeView->hideColumn( i ); } m_parent->foldersTreeView->setRootIndex( m_folderFilterModel.mapFromSource( m_treeModel->index( bookmarkDocument() ))); m_parent->connect( m_parent->foldersTreeView, SIGNAL(clicked(QModelIndex)), m_parent, SLOT(handleFolderSelection(QModelIndex)) ); m_parent->connect( m_parent->foldersTreeView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), m_parent, SLOT(updateButtonState()) ); m_parent->connect( m_parent->renameFolderButton, SIGNAL(clicked(bool)), m_parent, SLOT(renameFolder()) ); m_parent->connect( m_parent->newFolderButton, SIGNAL(clicked(bool)), m_parent, SLOT(addNewFolder()) ); m_parent->connect( m_parent->removeFolderButton, SIGNAL(clicked(bool)), m_parent, SLOT(deleteFolder()) ); } void BookmarkManagerDialogPrivate::initializeBookmarksView( GeoDataTreeModel* treeModel ) { m_branchFilterModel.setSourceModel( treeModel ); m_parent->bookmarksListView->setModel( &m_branchFilterModel ); m_parent->bookmarksListView->setEditTriggers( QAbstractItemView::NoEditTriggers ); m_parent->connect( m_parent->bookmarksListView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), m_parent, SLOT(updateButtonState()) ); m_parent->connect( m_parent->editBookmarkButton, SIGNAL(clicked(bool)), m_parent, SLOT(editBookmark()) ); m_parent->connect( m_parent->removeBookmarkButton, SIGNAL(clicked(bool)), m_parent, SLOT(deleteBookmark()) ); } BookmarkManagerDialog::BookmarkManagerDialog( MarbleModel* model, QWidget *parent ) : QDialog( parent ), d( new BookmarkManagerDialogPrivate( this, model ) ) { setupUi( this ); bool const smallScreen = MarbleGlobal::getInstance()->profiles() & MarbleGlobal::SmallScreen; importButton->setVisible( !smallScreen ); exportButton->setVisible( !smallScreen ); foldersLabel->setVisible( !smallScreen ); bookmarkLabel->setVisible( !smallScreen ); d->initializeFoldersView( d->m_treeModel ); d->initializeBookmarksView( d->m_treeModel ); d->updateButtonState(); connect( this, SIGNAL(accepted()), SLOT(saveBookmarks()) ); connect( this, SIGNAL(rejected()), SLOT(discardChanges()) ); connect( exportButton, SIGNAL(clicked()), this, SLOT(exportBookmarks()) ); connect( importButton, SIGNAL(clicked()), this, SLOT(importBookmarks()) ); d->selectFolder(); } BookmarkManagerDialog::~BookmarkManagerDialog() { delete d; } void BookmarkManagerDialog::saveBookmarks() { d->m_manager->updateBookmarkFile(); } void BookmarkManagerDialog::exportBookmarks() { QString fileName = QFileDialog::getSaveFileName( this, tr( "Export Bookmarks" ), // krazy:exclude=qclasses QDir::homePath(), tr( "KML files (*.kml)" ) ); if ( !fileName.isEmpty() ) { if (!GeoDataDocumentWriter::write(fileName, *d->bookmarkDocument())) { mDebug() << "Could not write the bookmarks file" << fileName; QString const text = tr( "Unable to save bookmarks. Please check that the file is writable." ); QMessageBox::warning(this, tr("Bookmark Export"), text); } } } void BookmarkManagerDialog::importBookmarks() { QString const file = QFileDialog::getOpenFileName(this, tr("Import Bookmarks"), QDir::homePath(), tr( "KML Files (*.kml)" ) ); if ( file.isEmpty() ) { return; } GeoDataDocument *import = BookmarkManager::openFile( file ); if ( !import ) { QString const text = tr( "The file %1 cannot be opened as a KML file." ).arg( file ); QMessageBox::warning(this, tr( "Bookmark Import"), text); return; } GeoDataDocument *current = d->bookmarkDocument(); bool skipAll = false; bool replaceAll = false; d->importBookmarksRecursively(import, current, skipAll, replaceAll); d->selectFolder(); } void BookmarkManagerDialogPrivate::importBookmarksRecursively( GeoDataContainer *source, GeoDataContainer *destination, bool &replaceAll, bool &skipAll ) { for( GeoDataFolder *newFolder: source->folderList() ) { GeoDataFolder *existingFolder = m_manager->addNewBookmarkFolder(destination, newFolder->name()); importBookmarksRecursively(newFolder, existingFolder, skipAll, replaceAll); for( GeoDataPlacemark* newPlacemark: newFolder->placemarkList() ) { bool added = skipAll; GeoDataCoordinates newCoordinate = newPlacemark->coordinate(); GeoDataPlacemark *existingPlacemark = m_manager->bookmarkAt( m_manager->document(), newCoordinate ); if ( existingPlacemark ) { if ( skipAll ) { continue; } // Avoid message boxes for equal bookmarks, just skip them if ( existingPlacemark->name() == newPlacemark->name() && existingPlacemark->description() == newPlacemark->description() ) { continue; } QPointer messageBox = new QMessageBox( m_parent ); QString const intro = tr( "The file contains a bookmark that already exists among your Bookmarks." ); QString const newBookmark = tr( "Imported bookmark" ); QString const existingBookmark = tr( "Existing bookmark" ); QString const question = tr( "Do you want to replace the existing bookmark with the imported one?" ); QString html = QLatin1String("

%1

" "
%2%3 / %4
%5%6 / %7

%8

"); - html = html.arg( intro ).arg( existingBookmark ).arg( existingFolder->name() ); - html = html.arg( existingPlacemark->name() ).arg( newBookmark ).arg( newFolder->name() ); - html = html.arg( newPlacemark->name() ).arg( question ); + html = html.arg( intro, existingBookmark, existingFolder->name(), + existingPlacemark->name(), newBookmark, newFolder->name(), + newPlacemark->name(), question ); messageBox->setText( html ); QAbstractButton *replaceButton = messageBox->addButton(tr( "Replace" ), QMessageBox::ActionRole ); QAbstractButton *replaceAllButton = messageBox->addButton(tr( "Replace All" ), QMessageBox::ActionRole ); QAbstractButton *skipButton = messageBox->addButton(tr( "Skip" ), QMessageBox::ActionRole ); QAbstractButton *skipAllButton = messageBox->addButton(tr( "Skip All" ), QMessageBox::ActionRole ); messageBox->addButton( QMessageBox::Cancel ); messageBox->setIcon( QMessageBox::Question ); if ( !replaceAll ) { messageBox->exec(); } if ( messageBox->clickedButton() == replaceAllButton ) { replaceAll = true; } else if ( messageBox->clickedButton() == skipAllButton ) { skipAll = true; added = true; } else if ( messageBox->clickedButton() == skipButton ) { delete messageBox; continue; } else if ( messageBox->clickedButton() != replaceButton ) { delete messageBox; return; } if ( messageBox->clickedButton() == replaceButton || replaceAll ) { m_manager->removeBookmark( existingPlacemark ); m_manager->addBookmark( existingFolder, *newPlacemark ); mDebug() << "Placemark " << newPlacemark->name() << " replaces " << existingPlacemark->name(); delete messageBox; break; } delete messageBox; } if ( !added ) { m_manager->addBookmark( existingFolder, *newPlacemark ); } } } } GeoDataDocument* BookmarkManagerDialogPrivate::bookmarkDocument() { return m_manager->document(); } void BookmarkManagerDialog::setButtonBoxVisible( bool visible ) { buttonBox->setVisible( visible ); if ( !visible ) { disconnect( this, SIGNAL(rejected()), this, SLOT(discardChanges()) ); connect( this, SIGNAL(rejected()), SLOT(saveBookmarks()) ); } } } #include "moc_BookmarkManagerDialog.cpp" diff --git a/src/lib/marble/CurrentLocationWidget.cpp b/src/lib/marble/CurrentLocationWidget.cpp index 6e931f163..4aec22a4a 100644 --- a/src/lib/marble/CurrentLocationWidget.cpp +++ b/src/lib/marble/CurrentLocationWidget.cpp @@ -1,460 +1,460 @@ // // This file is part of the Marble Virtual Globe. // // This program is free software licensed under the GNU LGPL. You can // find a copy of this license in LICENSE.txt in the top directory of // the source code. // // Copyright 2004-2007 Torsten Rahn // Copyright 2007 Inge Wallin // Copyright 2007 Thomas Zander // Copyright 2010 Bastian Holst // // Self #include "CurrentLocationWidget.h" // Marble #include "AutoNavigation.h" #include "MarbleDebug.h" #include "MarbleLocale.h" #include "MarbleModel.h" #include "MarbleWidget.h" #include "MarbleWidgetPopupMenu.h" #include "GeoDataCoordinates.h" #include "PositionProviderPlugin.h" #include "PluginManager.h" #include "PositionTracking.h" #include "routing/RoutingManager.h" using namespace Marble; /* TRANSLATOR Marble::CurrentLocationWidget */ // Ui #include "ui_CurrentLocationWidget.h" #include #include #include namespace Marble { class CurrentLocationWidgetPrivate { public: CurrentLocationWidgetPrivate(); Ui::CurrentLocationWidget m_currentLocationUi; MarbleWidget *m_widget; AutoNavigation *m_adjustNavigation; QList m_positionProviderPlugins; GeoDataCoordinates m_currentPosition; QString m_lastOpenPath; QString m_lastSavePath; void receiveGpsCoordinates( const GeoDataCoordinates &position, qreal speed ); void adjustPositionTrackingStatus( PositionProviderStatus status ); void changePositionProvider( const QString &provider ); void trackPlacemark(); void centerOnCurrentLocation(); void updateRecenterComboBox( AutoNavigation::CenterMode centerMode ); void updateAutoZoomCheckBox( bool autoZoom ); void updateActivePositionProvider( PositionProviderPlugin* ); void updateGuidanceMode(); void saveTrack(); void openTrack(); void clearTrack(); }; CurrentLocationWidgetPrivate::CurrentLocationWidgetPrivate() : m_widget( 0 ), m_adjustNavigation( 0 ), m_positionProviderPlugins(), m_currentPosition(), m_lastOpenPath(), m_lastSavePath() { } CurrentLocationWidget::CurrentLocationWidget( QWidget *parent, Qt::WindowFlags f ) : QWidget( parent, f ), d( new CurrentLocationWidgetPrivate() ) { d->m_currentLocationUi.setupUi( this ); layout()->setMargin( 0 ); connect( d->m_currentLocationUi.recenterComboBox, SIGNAL (currentIndexChanged(int)), this, SLOT(setRecenterMode(int)) ); connect( d->m_currentLocationUi.autoZoomCheckBox, SIGNAL(clicked(bool)), this, SLOT(setAutoZoom(bool)) ); bool const smallScreen = MarbleGlobal::getInstance()->profiles() & MarbleGlobal::SmallScreen; d->m_currentLocationUi.positionTrackingComboBox->setVisible( !smallScreen ); d->m_currentLocationUi.locationLabel->setVisible( !smallScreen ); } CurrentLocationWidget::~CurrentLocationWidget() { delete d; } void CurrentLocationWidget::setMarbleWidget( MarbleWidget *widget ) { d->m_widget = widget; delete d->m_adjustNavigation; d->m_adjustNavigation = new AutoNavigation( widget->model(), widget->viewport(), this ); const PluginManager* pluginManager = d->m_widget->model()->pluginManager(); d->m_positionProviderPlugins = pluginManager->positionProviderPlugins(); for( const PositionProviderPlugin *plugin: d->m_positionProviderPlugins ) { d->m_currentLocationUi.positionTrackingComboBox->addItem( plugin->guiString() ); } if ( d->m_positionProviderPlugins.isEmpty() ) { d->m_currentLocationUi.positionTrackingComboBox->setEnabled( false ); QString html = "

No Position Tracking Plugin installed.

"; d->m_currentLocationUi.locationLabel->setText( html ); d->m_currentLocationUi.locationLabel->setEnabled ( true ); bool const hasTrack = !d->m_widget->model()->positionTracking()->isTrackEmpty(); d->m_currentLocationUi.showTrackCheckBox->setEnabled( hasTrack ); d->m_currentLocationUi.saveTrackButton->setEnabled( hasTrack ); d->m_currentLocationUi.clearTrackButton->setEnabled( hasTrack ); } //disconnect CurrentLocation Signals disconnect( d->m_widget->model()->positionTracking(), SIGNAL(gpsLocation(GeoDataCoordinates,qreal)), this, SLOT(receiveGpsCoordinates(GeoDataCoordinates,qreal)) ); disconnect( d->m_widget->model()->positionTracking(), SIGNAL(positionProviderPluginChanged(PositionProviderPlugin*)), this, SLOT(updateActivePositionProvider(PositionProviderPlugin*)) ); disconnect( d->m_currentLocationUi.positionTrackingComboBox, SIGNAL(currentIndexChanged(QString)), this, SLOT(changePositionProvider(QString)) ); disconnect( d->m_currentLocationUi.locationLabel, SIGNAL(linkActivated(QString)), this, SLOT(centerOnCurrentLocation()) ); disconnect( d->m_widget->model()->positionTracking(), SIGNAL(statusChanged(PositionProviderStatus)),this, SLOT(adjustPositionTrackingStatus(PositionProviderStatus)) ); disconnect( d->m_widget->model(), SIGNAL(trackedPlacemarkChanged(const GeoDataPlacemark*)), this, SLOT(trackPlacemark()) ); //connect CurrentLocation signals connect( d->m_widget->model()->positionTracking(), SIGNAL(gpsLocation(GeoDataCoordinates,qreal)), this, SLOT(receiveGpsCoordinates(GeoDataCoordinates,qreal)) ); connect( d->m_widget->model()->positionTracking(), SIGNAL(positionProviderPluginChanged(PositionProviderPlugin*)), this, SLOT(updateActivePositionProvider(PositionProviderPlugin*)) ); d->updateActivePositionProvider( d->m_widget->model()->positionTracking()->positionProviderPlugin() ); connect( d->m_currentLocationUi.positionTrackingComboBox, SIGNAL(currentIndexChanged(QString)), this, SLOT(changePositionProvider(QString)) ); connect( d->m_currentLocationUi.locationLabel, SIGNAL(linkActivated(QString)), this, SLOT(centerOnCurrentLocation()) ); connect( d->m_widget->model()->positionTracking(), SIGNAL(statusChanged(PositionProviderStatus)), this, SLOT(adjustPositionTrackingStatus(PositionProviderStatus)) ); connect( d->m_adjustNavigation, SIGNAL(recenterModeChanged(AutoNavigation::CenterMode)), this, SLOT(updateRecenterComboBox(AutoNavigation::CenterMode)) ); connect( d->m_adjustNavigation, SIGNAL(autoZoomToggled(bool)), this, SLOT(updateAutoZoomCheckBox(bool)) ); connect( d->m_adjustNavigation, SIGNAL(zoomIn(FlyToMode)), d->m_widget, SLOT(zoomIn(FlyToMode)) ); connect( d->m_adjustNavigation, SIGNAL(zoomOut(FlyToMode)), d->m_widget, SLOT(zoomOut(FlyToMode)) ); connect( d->m_adjustNavigation, SIGNAL(centerOn(GeoDataCoordinates,bool)), d->m_widget, SLOT(centerOn(GeoDataCoordinates,bool)) ); connect( d->m_widget, SIGNAL(visibleLatLonAltBoxChanged(GeoDataLatLonAltBox)), d->m_adjustNavigation, SLOT(inhibitAutoAdjustments()) ); connect( d->m_widget->model()->routingManager(), SIGNAL(guidanceModeEnabledChanged(bool)), this, SLOT(updateGuidanceMode()) ); connect (d->m_currentLocationUi.showTrackCheckBox, SIGNAL(clicked(bool)), d->m_widget->model()->positionTracking(), SLOT(setTrackVisible(bool))); connect (d->m_currentLocationUi.showTrackCheckBox, SIGNAL(clicked(bool)), d->m_widget, SLOT(update())); if ( d->m_widget->model()->positionTracking()->trackVisible() ) { d->m_currentLocationUi.showTrackCheckBox->setCheckState(Qt::Checked); } connect ( d->m_currentLocationUi.saveTrackButton, SIGNAL(clicked(bool)), this, SLOT(saveTrack())); connect ( d->m_currentLocationUi.openTrackButton, SIGNAL(clicked(bool)), this, SLOT(openTrack())); connect (d->m_currentLocationUi.clearTrackButton, SIGNAL(clicked(bool)), this, SLOT(clearTrack())); connect( d->m_widget->model(), SIGNAL(trackedPlacemarkChanged(const GeoDataPlacemark*)), this, SLOT(trackPlacemark()) ); } void CurrentLocationWidgetPrivate::adjustPositionTrackingStatus( PositionProviderStatus status ) { if ( status == PositionProviderStatusAvailable ) { return; } QString html = "

"; switch ( status ) { case PositionProviderStatusUnavailable: html += QObject::tr( "No position available." ); break; case PositionProviderStatusAcquiring: html += QObject::tr( "Waiting for current location information..." ); break; case PositionProviderStatusAvailable: Q_ASSERT( false ); break; case PositionProviderStatusError: html += QObject::tr( "Error when determining current location: " ); html += m_widget->model()->positionTracking()->error(); break; } html += QLatin1String("

"); m_currentLocationUi.locationLabel->setEnabled( true ); m_currentLocationUi.locationLabel->setText( html ); } void CurrentLocationWidgetPrivate::updateActivePositionProvider( PositionProviderPlugin *plugin ) { m_currentLocationUi.positionTrackingComboBox->blockSignals( true ); if ( !plugin ) { m_currentLocationUi.positionTrackingComboBox->setCurrentIndex( 0 ); } else { for( int i=0; icount(); ++i ) { if ( m_currentLocationUi.positionTrackingComboBox->itemText( i ) == plugin->guiString() ) { m_currentLocationUi.positionTrackingComboBox->setCurrentIndex( i ); break; } } } m_currentLocationUi.positionTrackingComboBox->blockSignals( false ); m_currentLocationUi.recenterLabel->setEnabled( plugin ); m_currentLocationUi.recenterComboBox->setEnabled( plugin ); m_currentLocationUi.autoZoomCheckBox->setEnabled( plugin ); } void CurrentLocationWidgetPrivate::updateGuidanceMode() { const bool enabled = m_widget->model()->routingManager()->guidanceModeEnabled(); m_adjustNavigation->setAutoZoom( enabled ); m_adjustNavigation->setRecenter( enabled ? AutoNavigation::RecenterOnBorder : AutoNavigation::DontRecenter ); } void CurrentLocationWidgetPrivate::receiveGpsCoordinates( const GeoDataCoordinates &position, qreal speed ) { m_currentPosition = position; QString unitString; QString altitudeUnitString; QString distanceUnitString; qreal unitSpeed = 0.0; qreal altitude = 0.0; qreal length = m_widget->model()->positionTracking()->length( m_widget->model()->planetRadius() ); QString html = QLatin1String("" "" "" "" "" "" "" "
Longitude%1
Latitude%2
Altitude%3
Speed%4
Distance%5
" ""); switch ( MarbleGlobal::getInstance()->locale()->measurementSystem() ) { case MarbleLocale::MetricSystem: //kilometers per hour unitString = QObject::tr("km/h"); unitSpeed = speed * HOUR2SEC * METER2KM; altitudeUnitString = QObject::tr("m"); distanceUnitString = QObject::tr("m"); if ( length > 1000.0 ) { length /= 1000.0; distanceUnitString = QObject::tr("km"); } altitude = position.altitude(); break; case MarbleLocale::ImperialSystem: //miles per hour unitString = QObject::tr("m/h"); unitSpeed = speed * HOUR2SEC * METER2KM * KM2MI; altitudeUnitString = QObject::tr("ft"); distanceUnitString = QObject::tr("ft"); altitude = position.altitude() * M2FT; length *= M2FT; break; case MarbleLocale::NauticalSystem: // nautical miles unitString = QObject::tr("kt"); unitSpeed = speed * HOUR2SEC * METER2KM * KM2NM; altitudeUnitString = QObject::tr("m"); distanceUnitString = QObject::tr("nm"); altitude = position.altitude(); length *= METER2KM*KM2NM; break; } // TODO read this value from the incoming signal const QString speedString = QLocale::system().toString( unitSpeed, 'f', 1); const QString altitudeString = QString( "%1 %2" ).arg( altitude, 0, 'f', 1, QChar(' ') ).arg( altitudeUnitString ); const QString distanceString = QString( "%1 %2" ).arg( length, 0, 'f', 1, QChar(' ') ).arg( distanceUnitString ); - html = html.arg( position.lonToString() ).arg( position.latToString() ); - html = html.arg(altitudeString).arg(speedString + QLatin1Char(' ') + unitString); + html = html.arg( position.lonToString(), position.latToString() ); + html = html.arg(altitudeString, speedString + QLatin1Char(' ') + unitString); html = html.arg( distanceString ); m_currentLocationUi.locationLabel->setText( html ); m_currentLocationUi.showTrackCheckBox->setEnabled( true ); m_currentLocationUi.saveTrackButton->setEnabled( true ); m_currentLocationUi.clearTrackButton->setEnabled( true ); } void CurrentLocationWidgetPrivate::changePositionProvider( const QString &provider ) { for( const PositionProviderPlugin* plugin: m_positionProviderPlugins ) { if ( plugin->guiString() == provider ) { m_currentLocationUi.locationLabel->setEnabled( true ); PositionProviderPlugin* instance = plugin->newInstance(); PositionTracking *tracking = m_widget->model()->positionTracking(); tracking->setPositionProviderPlugin( instance ); m_widget->update(); return; } } // requested provider not found -> disable position tracking m_currentLocationUi.locationLabel->setEnabled( false ); m_widget->model()->positionTracking()->setPositionProviderPlugin( 0 ); m_widget->update(); } void CurrentLocationWidgetPrivate::trackPlacemark() { changePositionProvider( QObject::tr( "Placemark" ) ); m_adjustNavigation->setRecenter( AutoNavigation::AlwaysRecenter ); } void CurrentLocationWidget::setRecenterMode( int mode ) { if ( mode >= 0 && mode <= AutoNavigation::RecenterOnBorder ) { AutoNavigation::CenterMode centerMode = ( AutoNavigation::CenterMode ) mode; d->m_adjustNavigation->setRecenter( centerMode ); } } void CurrentLocationWidget::setAutoZoom( bool autoZoom ) { d->m_adjustNavigation->setAutoZoom( autoZoom ); } void CurrentLocationWidgetPrivate::updateAutoZoomCheckBox( bool autoZoom ) { m_currentLocationUi.autoZoomCheckBox->setChecked( autoZoom ); } void CurrentLocationWidgetPrivate::updateRecenterComboBox( AutoNavigation::CenterMode centerMode ) { m_currentLocationUi.recenterComboBox->setCurrentIndex( centerMode ); } void CurrentLocationWidgetPrivate::centerOnCurrentLocation() { m_widget->centerOn(m_currentPosition, true); } void CurrentLocationWidgetPrivate::saveTrack() { QString suggested = m_lastSavePath; QString fileName = QFileDialog::getSaveFileName(m_widget, QObject::tr("Save Track"), // krazy:exclude=qclasses suggested.append(QLatin1Char('/') + QDateTime::currentDateTime().toString("yyyy-MM-dd_hhmmss") + QLatin1String(".kml")), QObject::tr("KML File (*.kml)")); if ( fileName.isEmpty() ) { return; } if ( !fileName.endsWith(QLatin1String( ".kml" ), Qt::CaseInsensitive) ) { fileName += QLatin1String(".kml"); } QFileInfo file( fileName ); m_lastSavePath = file.absolutePath(); m_widget->model()->positionTracking()->saveTrack( fileName ); } void CurrentLocationWidgetPrivate::openTrack() { QString suggested = m_lastOpenPath; QString fileName = QFileDialog::getOpenFileName( m_widget, QObject::tr("Open Track"), // krazy:exclude=qclasses suggested, QObject::tr("KML File (*.kml)")); if ( !fileName.isEmpty() ) { QFileInfo file( fileName ); m_lastOpenPath = file.absolutePath(); m_widget->model()->addGeoDataFile( fileName ); } } void CurrentLocationWidgetPrivate::clearTrack() { const int result = QMessageBox::question( m_widget, QObject::tr( "Clear current track" ), QObject::tr( "Are you sure you want to clear the current track?" ), QMessageBox::Yes, QMessageBox::No ); if ( result == QMessageBox::Yes ) { m_widget->model()->positionTracking()->clearTrack(); m_widget->update(); m_currentLocationUi.saveTrackButton->setEnabled( false ); m_currentLocationUi.clearTrackButton->setEnabled( false ); } } AutoNavigation::CenterMode CurrentLocationWidget::recenterMode() const { return d->m_adjustNavigation->recenterMode(); } bool CurrentLocationWidget::autoZoom() const { return d->m_adjustNavigation->autoZoom(); } bool CurrentLocationWidget::trackVisible() const { return d->m_widget->model()->positionTracking()->trackVisible(); } QString CurrentLocationWidget::lastOpenPath() const { return d->m_lastOpenPath; } QString CurrentLocationWidget::lastSavePath() const { return d->m_lastSavePath; } void CurrentLocationWidget::setTrackVisible( bool visible ) { d->m_currentLocationUi.showTrackCheckBox->setChecked( visible ); d->m_widget->model()->positionTracking()->setTrackVisible( visible ); } void CurrentLocationWidget::setLastOpenPath( const QString &path ) { d->m_lastOpenPath = path; } void CurrentLocationWidget::setLastSavePath( const QString &path ) { d->m_lastSavePath = path; } } #include "moc_CurrentLocationWidget.cpp" diff --git a/src/lib/marble/DownloadQueueSet.cpp b/src/lib/marble/DownloadQueueSet.cpp index 6a7e5a52d..732c8912f 100644 --- a/src/lib/marble/DownloadQueueSet.cpp +++ b/src/lib/marble/DownloadQueueSet.cpp @@ -1,279 +1,279 @@ // Copyright 2009 Jens-Michael Hoffmann // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library. If not, see . #include "DownloadQueueSet.h" #include "MarbleDebug.h" #include "HttpJob.h" namespace Marble { DownloadQueueSet::DownloadQueueSet( QObject * const parent ) : QObject( parent ) { } DownloadQueueSet::DownloadQueueSet( DownloadPolicy const & policy, QObject * const parent ) : QObject( parent ), m_downloadPolicy( policy ) { } DownloadQueueSet::~DownloadQueueSet() { // todo: delete HttpJobs } DownloadPolicy DownloadQueueSet::downloadPolicy() const { return m_downloadPolicy; } void DownloadQueueSet::setDownloadPolicy( DownloadPolicy const & policy ) { m_downloadPolicy = policy; } bool DownloadQueueSet::canAcceptJob( const QUrl& sourceUrl, const QString& destinationFileName ) const { if ( jobIsQueued( destinationFileName )) { mDebug() << "Download rejected: It's in the queue already:" << destinationFileName; return false; } if ( jobIsWaitingForRetry( destinationFileName )) { mDebug() << "Download rejected: Will try to download again in some time:" << destinationFileName; return false; } if ( jobIsActive( destinationFileName )) { mDebug() << "Download rejected: It's being downloaded already:" << destinationFileName; return false; } if ( jobIsBlackListed( sourceUrl )) { mDebug() << "Download rejected: Blacklisted."; return false; } return true; } void DownloadQueueSet::addJob( HttpJob * const job ) { m_jobs.push( job ); mDebug() << "addJob: new job queue size:" << m_jobs.count(); emit jobAdded(); emit progressChanged( m_activeJobs.size(), m_jobs.count() ); activateJobs(); } void DownloadQueueSet::activateJobs() { while ( !m_jobs.isEmpty() && m_activeJobs.count() < m_downloadPolicy.maximumConnections() ) { HttpJob * const job = m_jobs.pop(); activateJob( job ); } } void DownloadQueueSet::retryJobs() { while ( !m_retryQueue.isEmpty() ) { HttpJob * const job = m_retryQueue.dequeue(); mDebug() << "Requeuing" << job->destinationFileName(); // FIXME: addJob calls activateJobs every time addJob( job ); } } void DownloadQueueSet::purgeJobs() { // purge all waiting jobs while( !m_jobs.isEmpty() ) { HttpJob * const job = m_jobs.pop(); job->deleteLater(); } // purge all retry jobs qDeleteAll( m_retryQueue ); m_retryQueue.clear(); // cancel all current jobs while( !m_activeJobs.isEmpty() ) { deactivateJob( m_activeJobs.first() ); } emit progressChanged( m_activeJobs.size(), m_jobs.count() ); } void DownloadQueueSet::finishJob( HttpJob * job, const QByteArray& data ) { mDebug() << "finishJob: " << job->sourceUrl() << job->destinationFileName(); deactivateJob( job ); emit jobRemoved(); emit jobFinished( data, job->destinationFileName(), job->initiatorId() ); job->deleteLater(); activateJobs(); } void DownloadQueueSet::redirectJob( HttpJob * job, const QUrl& newSourceUrl ) { mDebug() << "jobRedirected:" << job->sourceUrl() << " -> " << newSourceUrl; deactivateJob( job ); emit jobRemoved(); emit jobRedirected( newSourceUrl, job->destinationFileName(), job->initiatorId(), job->downloadUsage() ); job->deleteLater(); } void DownloadQueueSet::retryOrBlacklistJob( HttpJob * job, const int errorCode ) { Q_ASSERT( errorCode != 0 ); Q_ASSERT( !m_retryQueue.contains( job )); deactivateJob( job ); emit jobRemoved(); if ( job->tryAgain() ) { mDebug() << QString( "Download of %1 to %2 failed, but trying again soon" ) - .arg( job->sourceUrl().toString() ).arg( job->destinationFileName() ); + .arg( job->sourceUrl().toString(), job->destinationFileName() ); m_retryQueue.enqueue( job ); emit jobRetry(); } else { mDebug() << "JOB-address: " << job << "Blacklist-size:" << m_jobBlackList.size() << "err:" << errorCode; m_jobBlackList.insert( job->sourceUrl().toString() ); mDebug() << QString( "Download of %1 Blacklisted. " "Number of blacklist items: %2" ) .arg( job->destinationFileName() ) .arg( m_jobBlackList.size() ); job->deleteLater(); } activateJobs(); } void DownloadQueueSet::activateJob( HttpJob * const job ) { m_activeJobs.push_back( job ); emit progressChanged( m_activeJobs.size(), m_jobs.count() ); connect( job, SIGNAL(jobDone(HttpJob*,int)), SLOT(retryOrBlacklistJob(HttpJob*,int))); connect( job, SIGNAL(redirected(HttpJob*,QUrl)), SLOT(redirectJob(HttpJob*,QUrl))); connect( job, SIGNAL(dataReceived(HttpJob*,QByteArray)), SLOT(finishJob(HttpJob*,QByteArray))); job->execute(); } /** pre condition: - job is in m_activeJobs - job's signal are connected to our slots post condition: - job is not in m_activeJobs anymore (and btw not in any other queue) - job's signals are disconnected from our slots */ void DownloadQueueSet::deactivateJob( HttpJob * const job ) { const bool disconnected = job->disconnect(); Q_ASSERT( disconnected ); Q_UNUSED( disconnected ); // for Q_ASSERT in release mode const bool removed = m_activeJobs.removeOne( job ); Q_ASSERT( removed ); Q_UNUSED( removed ); // for Q_ASSERT in release mode emit progressChanged( m_activeJobs.size(), m_jobs.count() ); } bool DownloadQueueSet::jobIsActive( QString const & destinationFileName ) const { QList::const_iterator pos = m_activeJobs.constBegin(); QList::const_iterator const end = m_activeJobs.constEnd(); for (; pos != end; ++pos) { if ( (*pos)->destinationFileName() == destinationFileName ) { return true; } } return false; } inline bool DownloadQueueSet::jobIsQueued( QString const & destinationFileName ) const { return m_jobs.contains( destinationFileName ); } bool DownloadQueueSet::jobIsWaitingForRetry( QString const & destinationFileName ) const { QList::const_iterator pos = m_retryQueue.constBegin(); QList::const_iterator const end = m_retryQueue.constEnd(); for (; pos != end; ++pos) { if ( (*pos)->destinationFileName() == destinationFileName ) { return true; } } return false; } bool DownloadQueueSet::jobIsBlackListed( const QUrl& sourceUrl ) const { QSet::const_iterator const pos = m_jobBlackList.constFind( sourceUrl.toString() ); return pos != m_jobBlackList.constEnd(); } inline bool DownloadQueueSet::JobStack::contains( const QString& destinationFileName ) const { return m_jobsContent.contains( destinationFileName ); } inline int DownloadQueueSet::JobStack::count() const { return m_jobs.count(); } inline bool DownloadQueueSet::JobStack::isEmpty() const { return m_jobs.isEmpty(); } inline HttpJob * DownloadQueueSet::JobStack::pop() { HttpJob * const job = m_jobs.pop(); bool const removed = m_jobsContent.remove( job->destinationFileName() ); Q_UNUSED( removed ); // for Q_ASSERT in release mode Q_ASSERT( removed ); return job; } inline void DownloadQueueSet::JobStack::push( HttpJob * const job ) { m_jobs.push( job ); m_jobsContent.insert( job->destinationFileName() ); } } #include "moc_DownloadQueueSet.cpp" diff --git a/src/lib/marble/MapViewItemDelegate.cpp b/src/lib/marble/MapViewItemDelegate.cpp index cae5e69af..31d7a619c 100644 --- a/src/lib/marble/MapViewItemDelegate.cpp +++ b/src/lib/marble/MapViewItemDelegate.cpp @@ -1,112 +1,112 @@ // // This file is part of the Marble Virtual Globe. // // This program is free software licensed under the GNU LGPL. You can // find a copy of this license in LICENSE.txt in the top directory of // the source code. // // Copyright 2004-2007 Torsten Rahn // Copyright 2007 Inge Wallin // Copyright 2007 Thomas Zander // Copyright 2010 Bastian Holst // Coprright 2011-2013 Bernhard Beschow // Copyright 2012 Illya Kovalevskyy // //Self #include "MapViewItemDelegate.h" //Qt #include #include #include #include #include #include #include namespace Marble { MapViewItemDelegate::MapViewItemDelegate( QListView *view ) : m_view(view), m_bookmarkIcon(QStringLiteral(":/icons/bookmarks.png")) { // nothing to do } void MapViewItemDelegate::paint( QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index ) const { QStyleOptionViewItem styleOption = option; initStyleOption( &styleOption, index ); styleOption.text = QString(); styleOption.icon = QIcon(); bool const selected = styleOption.state & QStyle::State_Selected; bool const active = styleOption.state & QStyle::State_Active; bool const hover = styleOption.state & QStyle::State_MouseOver; QPalette::ColorGroup const colorGroup = active ? QPalette::Active : QPalette::Inactive; if ( selected || hover ) { styleOption.features &= ~QStyleOptionViewItemV2::Alternate; QPalette::ColorRole colorRole = selected ? QPalette::Highlight : QPalette::Midlight; painter->fillRect( styleOption.rect, styleOption.palette.color( colorGroup, colorRole ) ); } QStyle* style = styleOption.widget ? styleOption.widget->style() : QApplication::style(); style->drawControl( QStyle::CE_ItemViewItem, &styleOption, painter, styleOption.widget ); QRect const rect = styleOption.rect; QSize const iconSize = styleOption.decorationSize; QRect const iconRect( rect.topLeft(), iconSize ); QIcon const icon = index.data( Qt::DecorationRole ).value(); painter->drawPixmap( iconRect, icon.pixmap( iconSize ) ); int const padding = 5; QString const name = index.data().toString(); const bool isFavorite = QSettings().contains(QLatin1String("Favorites/") + name); QSize const bookmarkSize( 16, 16 ); QRect bookmarkRect( iconRect.bottomRight(), bookmarkSize ); bookmarkRect.translate( QPoint( -bookmarkSize.width() - padding, -bookmarkSize.height() - padding ) ); QIcon::Mode mode = isFavorite ? QIcon::Normal : QIcon::Disabled; painter->drawPixmap( bookmarkRect, m_bookmarkIcon.pixmap( bookmarkSize, mode ) ); QTextDocument document; document.setTextWidth( rect.width() - iconSize.width() - padding ); document.setDefaultFont( styleOption.font ); document.setHtml( text( index ) ); QRect textRect = QRect( iconRect.topRight(), QSize( document.textWidth() - padding, rect.height() - padding ) ); painter->save(); painter->translate( textRect.topLeft() ); painter->setClipRect( textRect.translated( -textRect.topLeft() ) ); QAbstractTextDocumentLayout::PaintContext paintContext; paintContext.palette = styleOption.palette; QPalette::ColorRole const role = selected && active ? QPalette::HighlightedText : QPalette::Text; paintContext.palette.setColor( QPalette::Text, styleOption.palette.color( colorGroup, role ) ); document.documentLayout()->draw( painter, paintContext ); painter->restore(); } QSize MapViewItemDelegate::sizeHint( const QStyleOptionViewItem &option, const QModelIndex &index ) const { if ( index.column() == 0 ) { QSize const iconSize = option.decorationSize; QTextDocument doc; doc.setDefaultFont( option.font ); doc.setTextWidth( m_view->width() - iconSize.width() - 10 ); doc.setHtml( text( index ) ); return QSize( iconSize.width() + doc.size().width(), iconSize.height() ); } return QSize(); } QString MapViewItemDelegate::text( const QModelIndex &index ) { QString const title = index.data().toString(); QString const description = index.data( Qt::UserRole+2 ).toString(); - return QString("

%1

%2").arg( title ).arg( description ); + return QString("

%1

%2").arg( title, description ); } } #include "moc_MapViewItemDelegate.cpp" diff --git a/src/lib/marble/MarbleDirs.cpp b/src/lib/marble/MarbleDirs.cpp index 3b160b44a..dc48da58c 100644 --- a/src/lib/marble/MarbleDirs.cpp +++ b/src/lib/marble/MarbleDirs.cpp @@ -1,351 +1,351 @@ // // This file is part of the Marble Virtual Globe. // // This program is free software licensed under the GNU LGPL. You can // find a copy of this license in LICENSE.txt in the top directory of // the source code. // // Copyright 2004-2007 Torsten Rahn // Copyright 2007 Inge Wallin // #include "MarbleDirs.h" #include "MarbleDebug.h" #include #include #include #include #include #include #include #ifdef Q_OS_WIN //for getting appdata path //mingw-w64 Internet Explorer 5.01 #define _WIN32_IE 0x0501 #include #endif #ifdef Q_OS_MACX //for getting app bundle path #include #endif #include using namespace Marble; namespace { QString runTimeMarbleDataPath; QString runTimeMarblePluginPath; } MarbleDirs::MarbleDirs() : d( 0 ) { } QString MarbleDirs::path( const QString& relativePath ) { QString localpath = localPath() + QLatin1Char('/') + relativePath; // local path QString systempath = systemPath() + QLatin1Char('/') + relativePath; // system path QString fullpath = systempath; if ( QFile::exists( localpath ) ) { fullpath = localpath; } return QDir( fullpath ).canonicalPath(); } QString MarbleDirs::pluginPath( const QString& relativePath ) { const QString localpath = pluginLocalPath() + QDir::separator() + relativePath; // local path const QString systempath = pluginSystemPath() + QDir::separator() + relativePath; // system path QString fullpath = systempath; if ( QFile::exists( localpath ) ) { fullpath = localpath; } return QDir( fullpath ).canonicalPath(); } QStringList MarbleDirs::entryList( const QString& relativePath, QDir::Filters filters ) { QStringList filesLocal = QDir(MarbleDirs::localPath() + QLatin1Char('/') + relativePath).entryList(filters); QStringList filesSystem = QDir(MarbleDirs::systemPath() + QLatin1Char('/') + relativePath).entryList(filters); QStringList allFiles( filesLocal ); allFiles << filesSystem; // remove duplicate entries allFiles.sort(); for ( int i = 1; i < allFiles.size(); ++i ) { if ( allFiles.at(i) == allFiles.at( i - 1 ) ) { allFiles.removeAt(i); --i; } } return allFiles; } QStringList MarbleDirs::pluginEntryList( const QString& relativePath, QDir::Filters filters ) { QStringList allFiles = QDir(MarbleDirs::pluginLocalPath() + QLatin1Char('/') + relativePath).entryList(filters); auto const pluginSystemPath = MarbleDirs::pluginSystemPath(); if (!pluginSystemPath.isEmpty()) { allFiles << QDir(pluginSystemPath + QLatin1Char('/') + relativePath).entryList(filters); } // remove duplicate entries allFiles.sort(); for ( int i = 1; i < allFiles.size(); ++i ) { if ( allFiles.at(i) == allFiles.at( i - 1 ) ) { allFiles.removeAt(i); --i; } } return allFiles; } QString MarbleDirs::systemPath() { if (!runTimeMarbleDataPath.isEmpty()) { return runTimeMarbleDataPath; } QString systempath; #ifdef Q_OS_WIN return QCoreApplication::applicationDirPath() + QDir::separator() + QLatin1String("data"); #endif #ifdef Q_OS_MACX // // On OSX lets try to find any file first in the bundle // before branching out to home and sys dirs // CFURLRef myBundleRef = CFBundleCopyBundleURL(CFBundleGetMainBundle()); CFStringRef myMacPath = CFURLCopyFileSystemPath(myBundleRef, kCFURLPOSIXPathStyle); const char *mypPathPtr = CFStringGetCStringPtr(myMacPath,CFStringGetSystemEncoding()); CFRelease(myBundleRef); QString myPath(mypPathPtr); CFRelease(myMacPath); //do some magick so that we can still find data dir if //marble was not built as a bundle if (myPath.contains(QLatin1String(".app"))) { //its a bundle! systempath = myPath + QLatin1String("/Contents/Resources/data"); } if ( QFile::exists( systempath ) ){ return systempath; } #endif // mac bundle #ifdef Q_OS_ANDROID systempath = "assets:/data"; return systempath; #endif #ifdef MARBLE_DATA_PATH //MARBLE_DATA_PATH is a compiler define set by cmake QString compileTimeMarbleDataPath(MARBLE_DATA_PATH); if(QDir(compileTimeMarbleDataPath).exists()) return compileTimeMarbleDataPath; #endif // MARBLE_DATA_PATH return QDir( QCoreApplication::applicationDirPath() // TODO: QTONLY definition was removed during Qt5/KF5 port, check what code should do #if defined(QTONLY) + QLatin1String( "/data" ) #else + QLatin1String( "/../share/apps/marble/data" ) #endif ).canonicalPath(); } QString MarbleDirs::pluginSystemPath() { if (!runTimeMarblePluginPath.isEmpty()) { return runTimeMarblePluginPath; } QString systempath; #ifdef Q_OS_MACX // // On OSX lets try to find any file first in the bundle // before branching out to home and sys dirs // CFURLRef myBundleRef = CFBundleCopyBundleURL(CFBundleGetMainBundle()); CFStringRef myMacPath = CFURLCopyFileSystemPath(myBundleRef, kCFURLPOSIXPathStyle); const char *mypPathPtr = CFStringGetCStringPtr(myMacPath,CFStringGetSystemEncoding()); CFRelease(myBundleRef); CFRelease(myMacPath); QString myPath(mypPathPtr); //do some magick so that we can still find data dir if //marble was not built as a bundle if (myPath.contains(QLatin1String(".app"))) { //its a bundle! systempath = myPath + QLatin1String("/Contents/Resources/plugins"); } if ( QFile::exists( systempath ) ){ return systempath; } #endif // mac bundle #ifdef Q_OS_WIN return QCoreApplication::applicationDirPath() + QDir::separator() + QLatin1String("plugins"); #endif #ifdef Q_OS_ANDROID return "assets:/plugins"; #endif #ifdef MARBLE_PLUGIN_PATH //MARBLE_PLUGIN_PATH is a compiler define set by cmake QString compileTimeMarblePluginPath(MARBLE_PLUGIN_PATH); if(QDir(compileTimeMarblePluginPath).exists()) return compileTimeMarblePluginPath; #endif // MARBLE_PLUGIN_PATH return QDir( QCoreApplication::applicationDirPath() // TODO: QTONLY definition was removed during Qt5/KF5 port, check what code should do #if defined(QTONLY) + QLatin1String( "/plugins" ) #else + QLatin1String( "/../lib/kde4/plugins/marble" ) #endif ).canonicalPath(); } QString MarbleDirs::localPath() { #ifndef Q_OS_WIN QString dataHome = getenv( "XDG_DATA_HOME" ); if( dataHome.isEmpty() ) dataHome = QDir::homePath() + QLatin1String("/.local/share"); return dataHome + QLatin1String("/marble"); // local path #else return QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/.marble/data"); #endif } QStringList MarbleDirs::oldLocalPaths() { QStringList possibleOldPaths; #ifndef Q_OS_WIN const QString oldDefault = QDir::homePath() + QLatin1String("/.marble/data"); possibleOldPaths.append( oldDefault ); const QString xdgDefault = QDir::homePath() + QLatin1String("/.local/share/marble"); possibleOldPaths.append( xdgDefault ); QString xdg = getenv( "XDG_DATA_HOME" ); xdg += QLatin1String("/marble/"); possibleOldPaths.append( xdg ); #endif #ifdef Q_OS_WIN HWND hwnd = 0; WCHAR *appdata_path = new WCHAR[MAX_PATH + 1]; SHGetSpecialFolderPathW(hwnd, appdata_path, CSIDL_APPDATA, 0); QString appdata = QString::fromUtf16(reinterpret_cast(appdata_path)); delete[] appdata_path; possibleOldPaths << QString(QDir::fromNativeSeparators(appdata) + QLatin1String("/.marble/data")); // local path #endif QString currentLocalPath = QDir( MarbleDirs::localPath() ).canonicalPath(); QStringList oldPaths; for( const QString& possibleOldPath: possibleOldPaths ) { if( !QDir().exists( possibleOldPath ) ) { continue; } QString canonicalPossibleOldPath = QDir( possibleOldPath ).canonicalPath(); if( canonicalPossibleOldPath == currentLocalPath ) { continue; } oldPaths.append( canonicalPossibleOldPath ); } return oldPaths; } QString MarbleDirs::pluginLocalPath() { #ifndef Q_OS_WIN return QDir::homePath() + QLatin1String("/.marble/plugins"); // local path #else return QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/.marble/plugins"); #endif } QString MarbleDirs::marbleDataPath() { return runTimeMarbleDataPath; } QString MarbleDirs::marblePluginPath() { return runTimeMarblePluginPath; } void MarbleDirs::setMarbleDataPath( const QString& adaptedPath ) { if ( !QDir::root().exists( adaptedPath ) ) { - qWarning() << QString( "Invalid MarbleDataPath \"%1\". Using \"%2\" instead." ).arg( adaptedPath ).arg( systemPath() ); + qWarning() << QString( "Invalid MarbleDataPath \"%1\". Using \"%2\" instead." ).arg( adaptedPath, systemPath() ); return; } runTimeMarbleDataPath = adaptedPath; } void MarbleDirs::setMarblePluginPath( const QString& adaptedPath ) { if ( !QDir::root().exists( adaptedPath ) ) { - qWarning() << QString( "Invalid MarblePluginPath \"%1\". Using \"%2\" instead." ).arg( adaptedPath ).arg( pluginSystemPath() ); + qWarning() << QString( "Invalid MarblePluginPath \"%1\". Using \"%2\" instead." ).arg( adaptedPath, pluginSystemPath() ); return; } runTimeMarblePluginPath = adaptedPath; } void MarbleDirs::debug() { mDebug() << "=== MarbleDirs: ==="; mDebug() << "Local Path:" << localPath(); mDebug() << "Plugin Local Path:" << pluginLocalPath(); mDebug() << ""; mDebug() << "Marble Data Path (Run Time) :" << runTimeMarbleDataPath; mDebug() << "Marble Data Path (Compile Time):" << QString(MARBLE_DATA_PATH); mDebug() << ""; mDebug() << "Marble Plugin Path (Run Time) :" << runTimeMarblePluginPath; mDebug() << "Marble Plugin Path (Compile Time):" << QString(MARBLE_PLUGIN_PATH); mDebug() << ""; mDebug() << "System Path:" << systemPath(); mDebug() << "Plugin System Path:" << pluginSystemPath(); mDebug() << "==================="; } diff --git a/src/lib/marble/RenderState.cpp b/src/lib/marble/RenderState.cpp index 6150c8278..eef93a544 100644 --- a/src/lib/marble/RenderState.cpp +++ b/src/lib/marble/RenderState.cpp @@ -1,135 +1,135 @@ // // This file is part of the Marble Virtual Globe. // // This program is free software licensed under the GNU LGPL. You can // find a copy of this license in LICENSE.txt in the top directory of // the source code. // // Copyright 2014 Dennis Nienhüser // #include "RenderState.h" #include namespace Marble { class Q_DECL_HIDDEN RenderState::Private { public: QString m_name; QList m_children; RenderStatus m_status; Private( const QString &name=QString(), RenderStatus status=Complete ); RenderStatus status() const; RenderStatus minimumStatus( RenderStatus a, RenderStatus b ) const; QString toString( const RenderState &state, int indent ) const; }; RenderState::RenderState( const QString &name, RenderStatus status ) : d( new Private( name, status ) ) { // nothing to do } RenderState::RenderState( const RenderState &other ) : d( new Private ) { *d = *other.d; } RenderState &RenderState::operator=( const RenderState &other ) { *d = *other.d; return *this; } RenderState::~RenderState() { delete d; } RenderStatus RenderState::status() const { return d->status(); } QString RenderState::name() const { return d->m_name; } int RenderState::children() const { return d->m_children.size(); } RenderState RenderState::childAt( int index ) const { return d->m_children.at( index ); } void RenderState::addChild(const RenderState &child) { d->m_children.push_back( child ); } RenderState::operator QString() const { return d->toString( *this, 0 ); } RenderState::Private::Private( const QString &name, RenderStatus status ) : m_name( name ), m_status( status ) { // nothing to do } RenderStatus RenderState::Private::status() const { RenderStatus status = Complete; for( const RenderState &child: m_children ) { status = minimumStatus( status, child.status() ); } return minimumStatus( status, m_status ); } RenderStatus RenderState::Private::minimumStatus( RenderStatus a, RenderStatus b ) const { if ( a == Incomplete || b == Incomplete ) { return Incomplete; } if ( a == WaitingForData || b == WaitingForData ) { return WaitingForData; } if ( a == WaitingForUpdate || b == WaitingForUpdate ) { return WaitingForUpdate; } Q_ASSERT( a == Complete || b == Complete ); return Complete; } QString RenderState::Private::toString( const RenderState &state, int level ) const { QString const prefix = level > 0 ? "\n" : ""; QString const indent(level*2, QLatin1Char(' ')); QString status; switch ( state.status() ) { case Marble::Complete: status = "Complete"; break; case Marble::WaitingForUpdate: status = "Waiting for update"; break; case Marble::WaitingForData: status = "Waiting for data"; break; case Marble::Incomplete: status = "Incomplete"; break; } QString const name = ( state.name().isEmpty() ? "Anonymous renderer" : state.name() ); - QString result = QString("%1%2%3: %4").arg( prefix ).arg( indent ).arg( name ).arg( status ); + QString result = QString("%1%2%3: %4").arg( prefix, indent, name, status ); for( const RenderState &child: state.d->m_children ) { result += toString( child, level+1 ); } return result; } } diff --git a/src/lib/marble/RoutingRunner.cpp b/src/lib/marble/RoutingRunner.cpp index 81c046593..d93703dab 100644 --- a/src/lib/marble/RoutingRunner.cpp +++ b/src/lib/marble/RoutingRunner.cpp @@ -1,101 +1,101 @@ // // This file is part of the Marble Virtual Globe. // // This program is free software licensed under the GNU LGPL. You can // find a copy of this license in LICENSE.txt in the top directory of // the source code. // // Copyright 2012,2013 Bernhard Beschow #include "RoutingRunner.h" #include "GeoDataExtendedData.h" #include "GeoDataData.h" #include "MarbleGlobal.h" #include "MarbleLocale.h" #include #include #include namespace Marble { RoutingRunner::RoutingRunner( QObject *parent ) : QObject( parent ) { } const QString RoutingRunner::lengthString(qreal length) const { MarbleLocale::MeasurementSystem const measurementSystem = MarbleGlobal::getInstance()->locale()->measurementSystem(); int precision = 0; QString distanceUnit = tr( "m" ); switch ( measurementSystem ) { case MarbleLocale::ImperialSystem: precision = 1; distanceUnit = tr( "mi" ); length *= METER2KM * KM2MI; if ( length < 0.1 ) { length = 10 * qRound( length * 528 ); precision = 0; distanceUnit = tr( "ft" ); } break; case MarbleLocale::MetricSystem: if ( length >= 1000 ) { length *= METER2KM; distanceUnit = tr( "km" ); precision = 1; } else if ( length >= 200 ) { length = 50 * qRound( length / 50 ); } else if ( length >= 100 ) { length = 25 * qRound( length / 25 ); } else { length = 10 * qRound( length / 10 ); } break; case MarbleLocale::NauticalSystem: { length *= METER2KM * KM2NM; distanceUnit = tr( "nm" ); precision = length < 2.0 ? 2 : 1; } break; } return QString( "%1 %2" ).arg( length, 0, 'f', precision ).arg( distanceUnit ); } const QString RoutingRunner::durationString(const QTime& duration) const { const QString hoursString = duration.toString( "hh" ); const QString minutesString = duration.toString( "mm" ); - const QString timeString = tr("%1:%2 h","journey duration").arg( hoursString ).arg( minutesString ); + const QString timeString = tr("%1:%2 h","journey duration").arg( hoursString, minutesString ); return timeString; } const QString RoutingRunner::nameString(const QString& name, qreal length, const QTime& duration) const { const QString result = "%1; %2 (%3)"; - return result.arg( lengthString( length ) ).arg( durationString( duration ) ).arg( name ); + return result.arg( lengthString( length ), durationString( duration ), name ); } const GeoDataExtendedData RoutingRunner::routeData(qreal length, const QTime& duration) const { GeoDataExtendedData result; GeoDataData lengthData; lengthData.setName(QStringLiteral("length")); lengthData.setValue( length ); result.addValue( lengthData ); GeoDataData durationData; durationData.setName(QStringLiteral("duration")); durationData.setValue( duration.toString( Qt::ISODate ) ); result.addValue( durationData ); return result; } } #include "moc_RoutingRunner.cpp" diff --git a/src/lib/marble/ServerLayout.cpp b/src/lib/marble/ServerLayout.cpp index 37670e73b..8c865ef36 100644 --- a/src/lib/marble/ServerLayout.cpp +++ b/src/lib/marble/ServerLayout.cpp @@ -1,237 +1,237 @@ // // This file is part of the Marble Virtual Globe. // // This program is free software licensed under the GNU LGPL. You can // find a copy of this license in LICENSE.txt in the top directory of // the source code. // // Copyright 2010,2011 Bernhard Beschow // // Own #include "ServerLayout.h" #include "GeoSceneTileDataset.h" #include "GeoDataLatLonBox.h" #include "MarbleGlobal.h" #include "TileId.h" #include #include namespace Marble { ServerLayout::ServerLayout( GeoSceneTileDataset *textureLayer ) : m_textureLayer( textureLayer ) { } ServerLayout::~ServerLayout() { } MarbleServerLayout::MarbleServerLayout( GeoSceneTileDataset *textureLayer ) : ServerLayout( textureLayer ) { } QUrl MarbleServerLayout::downloadUrl( const QUrl &prototypeUrl, const TileId &id ) const { const QString path = QString( "%1/%2/%3/%3_%4.%5" ) .arg( prototypeUrl.path() ) .arg( id.zoomLevel() ) .arg(id.y(), tileDigits, 10, QLatin1Char('0')) .arg(id.x(), tileDigits, 10, QLatin1Char('0')) .arg( m_textureLayer->fileFormat().toLower() ); QUrl url = prototypeUrl; url.setPath( path ); return url; } QString MarbleServerLayout::name() const { return "Marble"; } QString ServerLayout::sourceDir() const { return m_textureLayer ? m_textureLayer->sourceDir() : QString(); } OsmServerLayout::OsmServerLayout( GeoSceneTileDataset *textureLayer ) : ServerLayout( textureLayer ) { } QUrl OsmServerLayout::downloadUrl( const QUrl &prototypeUrl, const TileId &id ) const { const QString suffix = m_textureLayer->fileFormat().toLower(); const QString path = QString( "%1/%2/%3.%4" ).arg( id.zoomLevel() ) .arg( id.x() ) .arg( id.y() ) .arg( suffix ); QUrl url = prototypeUrl; url.setPath( url.path() + path ); return url; } QString OsmServerLayout::name() const { return "OpenStreetMap"; } CustomServerLayout::CustomServerLayout( GeoSceneTileDataset *texture ) : ServerLayout( texture ) { } QUrl CustomServerLayout::downloadUrl( const QUrl &prototypeUrl, const TileId &id ) const { const GeoDataLatLonBox bbox = m_textureLayer->tileProjection()->geoCoordinates(id); QString urlStr = prototypeUrl.toString( QUrl::DecodeReserved ); urlStr.replace( "{zoomLevel}", QString::number( id.zoomLevel() ) ); urlStr.replace( "{x}", QString::number( id.x() ) ); urlStr.replace( "{y}", QString::number( id.y() ) ); urlStr.replace( "{west}", QString::number( bbox.west( GeoDataCoordinates::Degree ), 'f', 12 ) ); urlStr.replace( "{south}", QString::number( bbox.south( GeoDataCoordinates::Degree ), 'f', 12 ) ); urlStr.replace( "{east}", QString::number( bbox.east( GeoDataCoordinates::Degree ), 'f', 12 ) ); urlStr.replace( "{north}", QString::number( bbox.north( GeoDataCoordinates::Degree ), 'f', 12 ) ); return QUrl( urlStr ); } QString CustomServerLayout::name() const { return "Custom"; } WmsServerLayout::WmsServerLayout( GeoSceneTileDataset *texture ) : ServerLayout( texture ) { } QUrl WmsServerLayout::downloadUrl( const QUrl &prototypeUrl, const Marble::TileId &tileId ) const { const GeoDataLatLonBox box = m_textureLayer->tileProjection()->geoCoordinates(tileId); QUrlQuery url(prototypeUrl.query()); url.addQueryItem( "service", "WMS" ); url.addQueryItem( "request", "GetMap" ); url.addQueryItem( "version", "1.1.1" ); if ( !url.hasQueryItem( "styles" ) ) url.addQueryItem( "styles", "" ); if ( !url.hasQueryItem( "format" ) ) { if (m_textureLayer->fileFormat().toLower() == QLatin1String("jpg")) url.addQueryItem( "format", "image/jpeg" ); else url.addQueryItem("format", QLatin1String("image/") + m_textureLayer->fileFormat().toLower()); } if ( !url.hasQueryItem( "srs" ) ) { url.addQueryItem( "srs", epsgCode() ); } if ( !url.hasQueryItem( "layers" ) ) url.addQueryItem( "layers", m_textureLayer->name() ); url.addQueryItem( "width", QString::number( m_textureLayer->tileSize().width() ) ); url.addQueryItem( "height", QString::number( m_textureLayer->tileSize().height() ) ); - url.addQueryItem( "bbox", QString( "%1,%2,%3,%4" ).arg( QString::number( box.west( GeoDataCoordinates::Degree ), 'f', 12 ) ) - .arg( QString::number( box.south( GeoDataCoordinates::Degree ), 'f', 12 ) ) - .arg( QString::number( box.east( GeoDataCoordinates::Degree ), 'f', 12 ) ) - .arg( QString::number( box.north( GeoDataCoordinates::Degree ), 'f', 12 ) ) ); + url.addQueryItem( "bbox", QString( "%1,%2,%3,%4" ).arg( QString::number( box.west( GeoDataCoordinates::Degree ), 'f', 12 ), + QString::number( box.south( GeoDataCoordinates::Degree ), 'f', 12 ), + QString::number( box.east( GeoDataCoordinates::Degree ), 'f', 12 ), + QString::number( box.north( GeoDataCoordinates::Degree ), 'f', 12 ) ) ); QUrl finalUrl = prototypeUrl; finalUrl.setQuery(url); return finalUrl; } QString WmsServerLayout::name() const { return "WebMapService"; } QString WmsServerLayout::epsgCode() const { switch (m_textureLayer->tileProjectionType()) { case GeoSceneAbstractTileProjection::Equirectangular: return "EPSG:4326"; case GeoSceneAbstractTileProjection::Mercator: return "EPSG:3785"; } Q_ASSERT( false ); // not reached return QString(); } QuadTreeServerLayout::QuadTreeServerLayout( GeoSceneTileDataset *textureLayer ) : ServerLayout( textureLayer ) { } QUrl QuadTreeServerLayout::downloadUrl( const QUrl &prototypeUrl, const Marble::TileId &id ) const { QString urlStr = prototypeUrl.toString( QUrl::DecodeReserved ); urlStr.replace( "{quadIndex}", encodeQuadTree( id ) ); return QUrl( urlStr ); } QString QuadTreeServerLayout::name() const { return "QuadTree"; } QString QuadTreeServerLayout::encodeQuadTree( const Marble::TileId &id ) { QString tileNum; for ( int i = id.zoomLevel(); i >= 0; i-- ) { const int tileX = (id.x() >> i) % 2; const int tileY = (id.y() >> i) % 2; const int num = ( 2 * tileY ) + tileX; tileNum += QString::number( num ); } return tileNum; } TmsServerLayout::TmsServerLayout(GeoSceneTileDataset *textureLayer ) : ServerLayout( textureLayer ) { } QUrl TmsServerLayout::downloadUrl( const QUrl &prototypeUrl, const TileId &id ) const { const QString suffix = m_textureLayer->fileFormat().toLower(); // y coordinate in TMS start at the bottom of the map (South) and go upwards, // opposed to OSM which start at the top. // // http://wiki.osgeo.org/wiki/Tile_Map_Service_Specification int y_frombottom = ( 1< * Copyright 2010-2012 Bernhard Beschow * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #include "TileLoader.h" #include #include #include #include #include #include "GeoSceneTextureTileDataset.h" #include "GeoSceneTileDataset.h" #include "GeoSceneTypes.h" #include "GeoSceneVectorTileDataset.h" #include "GeoDataDocument.h" #include "HttpDownloadManager.h" #include "MarbleDebug.h" #include "MarbleDirs.h" #include "TileId.h" #include "TileLoaderHelper.h" #include "ParseRunnerPlugin.h" #include "ParsingRunner.h" Q_DECLARE_METATYPE( Marble::DownloadUsage ) namespace Marble { TileLoader::TileLoader(HttpDownloadManager * const downloadManager, const PluginManager *pluginManager) : m_pluginManager(pluginManager) { qRegisterMetaType( "DownloadUsage" ); connect( this, SIGNAL(downloadTile(QUrl,QString,QString,DownloadUsage)), downloadManager, SLOT(addJob(QUrl,QString,QString,DownloadUsage))); connect( downloadManager, SIGNAL(downloadComplete(QString,QString)), SLOT(updateTile(QString,QString))); connect( downloadManager, SIGNAL(downloadComplete(QByteArray,QString)), SLOT(updateTile(QByteArray,QString))); } TileLoader::~TileLoader() { // nothing to do } // If the tile image file is locally available: // - if not expired: create ImageTile, set state to "uptodate", return it => done // - if expired: create TextureTile, state is set to Expired by default, trigger dl, QImage TileLoader::loadTileImage( GeoSceneTextureTileDataset const *textureLayer, TileId const & tileId, DownloadUsage const usage ) { QString const fileName = tileFileName( textureLayer, tileId ); TileStatus status = tileStatus( textureLayer, tileId ); if ( status != Missing ) { // check if an update should be triggered if ( status == Available ) { mDebug() << Q_FUNC_INFO << tileId << "StateUptodate"; } else { Q_ASSERT( status == Expired ); mDebug() << Q_FUNC_INFO << tileId << "StateExpired"; triggerDownload( textureLayer, tileId, usage ); } QImage const image( fileName ); if ( !image.isNull() ) { // file is there, so create and return a tile object in any case return image; } } // tile was not locally available => trigger download and look for tiles in other levels // for scaling QImage replacementTile = scaledLowerLevelTile( textureLayer, tileId ); Q_ASSERT( !replacementTile.isNull() ); triggerDownload( textureLayer, tileId, usage ); return replacementTile; } GeoDataDocument *TileLoader::loadTileVectorData( GeoSceneVectorTileDataset const *textureLayer, TileId const & tileId, DownloadUsage const usage ) { // FIXME: textureLayer->fileFormat() could be used in the future for use just that parser, instead of all available parsers QString const fileName = tileFileName( textureLayer, tileId ); TileStatus status = tileStatus( textureLayer, tileId ); if ( status != Missing ) { // check if an update should be triggered if ( status == Available ) { mDebug() << Q_FUNC_INFO << tileId << "StateUptodate"; } else { Q_ASSERT( status == Expired ); mDebug() << Q_FUNC_INFO << tileId << "StateExpired"; triggerDownload( textureLayer, tileId, usage ); } QFile file ( fileName ); if ( file.exists() ) { // File is ready, so parse and return the vector data in any case GeoDataDocument* document = openVectorFile(fileName); if (document) { return document; } } } else { // tile was not locally available => trigger download triggerDownload( textureLayer, tileId, usage ); } return nullptr; } // This method triggers a download of the given tile (without checking // expiration). It is called by upper layer (StackedTileLoader) when the tile // that should be reloaded is currently loaded in memory. // // post condition // - download is triggered void TileLoader::downloadTile( GeoSceneTileDataset const *tileData, TileId const &tileId, DownloadUsage const usage ) { triggerDownload( tileData, tileId, usage ); } int TileLoader::maximumTileLevel( GeoSceneTileDataset const & tileData ) { // if maximum tile level is configured in the DGML files, // then use it, otherwise use old detection code. if ( tileData.maximumTileLevel() >= 0 ) { return tileData.maximumTileLevel(); } int maximumTileLevel = -1; const QFileInfo themeStr( tileData.themeStr() ); const QString tilepath = themeStr.isAbsolute() ? themeStr.absoluteFilePath() : MarbleDirs::path( tileData.themeStr() ); // mDebug() << "StackedTileLoader::maxPartialTileLevel tilepath" << tilepath; QStringList leveldirs = QDir( tilepath ).entryList( QDir::AllDirs | QDir::NoSymLinks | QDir::NoDotAndDotDot ); QStringList::const_iterator it = leveldirs.constBegin(); QStringList::const_iterator const end = leveldirs.constEnd(); for (; it != end; ++it ) { bool ok = true; const int value = (*it).toInt( &ok, 10 ); if ( ok && value > maximumTileLevel ) maximumTileLevel = value; } // mDebug() << "Detected maximum tile level that contains data: " // << maxtilelevel; return maximumTileLevel + 1; } bool TileLoader::baseTilesAvailable( GeoSceneTileDataset const & tileData ) { const int levelZeroColumns = tileData.levelZeroColumns(); const int levelZeroRows = tileData.levelZeroRows(); bool result = true; // Check whether the tiles from the lowest texture level are available // for ( int column = 0; result && column < levelZeroColumns; ++column ) { for ( int row = 0; result && row < levelZeroRows; ++row ) { const TileId id( 0, 0, column, row ); const QString tilepath = tileFileName( &tileData, id ); result &= QFile::exists( tilepath ); if (!result) { mDebug() << "Base tile " << tileData.relativeTileFileName( id ) << " is missing for source dir " << tileData.sourceDir(); } } } return result; } TileLoader::TileStatus TileLoader::tileStatus( GeoSceneTileDataset const *tileData, const TileId &tileId ) { QString const fileName = tileFileName( tileData, tileId ); QFileInfo fileInfo( fileName ); if ( !fileInfo.exists() ) { return Missing; } const QDateTime lastModified = fileInfo.lastModified(); const int expireSecs = tileData->expire(); const bool isExpired = lastModified.secsTo( QDateTime::currentDateTime() ) >= expireSecs; return isExpired ? Expired : Available; } void TileLoader::updateTile( QByteArray const & data, QString const & idStr ) { QStringList const components = idStr.split(QLatin1Char(':'), QString::SkipEmptyParts); Q_ASSERT( components.size() == 5 ); QString const origin = components[0]; QString const sourceDir = components[ 1 ]; int const zoomLevel = components[ 2 ].toInt(); int const tileX = components[ 3 ].toInt(); int const tileY = components[ 4 ].toInt(); TileId const id = TileId( sourceDir, zoomLevel, tileX, tileY ); if (origin == GeoSceneTypes::GeoSceneTextureTileType) { QImage const tileImage = QImage::fromData( data ); if ( tileImage.isNull() ) return; emit tileCompleted( id, tileImage ); } } void TileLoader::updateTile(const QString &fileName, const QString &idStr) { QStringList const components = idStr.split(QLatin1Char(':'), QString::SkipEmptyParts); Q_ASSERT( components.size() == 5 ); QString const origin = components[0]; QString const sourceDir = components[ 1 ]; int const zoomLevel = components[ 2 ].toInt(); int const tileX = components[ 3 ].toInt(); int const tileY = components[ 4 ].toInt(); TileId const id = TileId( sourceDir, zoomLevel, tileX, tileY ); if (origin == GeoSceneTypes::GeoSceneVectorTileType) { GeoDataDocument* document = openVectorFile(MarbleDirs::path(fileName)); if (document) { emit tileCompleted(id, document); } } } QString TileLoader::tileFileName( GeoSceneTileDataset const * tileData, TileId const & tileId ) { QString const fileName = tileData->relativeTileFileName( tileId ); QFileInfo const dirInfo( fileName ); return dirInfo.isAbsolute() ? fileName : MarbleDirs::path( fileName ); } void TileLoader::triggerDownload( GeoSceneTileDataset const *tileData, TileId const &id, DownloadUsage const usage ) { if (id.zoomLevel() > 0) { int minValue = tileData->maximumTileLevel() == -1 ? id.zoomLevel() : qMin( id.zoomLevel(), tileData->maximumTileLevel() ); if (id.zoomLevel() != qMax(tileData->minimumTileLevel(), minValue) ) { // Download only level 0 tiles and tiles between minimum and maximum tile level return; } } QUrl const sourceUrl = tileData->downloadUrl( id ); QString const destFileName = tileData->relativeTileFileName( id ); - QString const idStr = QString( "%1:%2:%3:%4:%5" ).arg( tileData->nodeType()).arg( tileData->sourceDir() ).arg( id.zoomLevel() ).arg( id.x() ).arg( id.y() ); + QString const idStr = QString( "%1:%2:%3:%4:%5" ).arg( tileData->nodeType(), tileData->sourceDir() ).arg( id.zoomLevel() ).arg( id.x() ).arg( id.y() ); emit downloadTile( sourceUrl, destFileName, idStr, usage ); } QImage TileLoader::scaledLowerLevelTile( const GeoSceneTextureTileDataset * textureData, TileId const & id ) { mDebug() << Q_FUNC_INFO << id; int const minimumLevel = textureData->minimumTileLevel(); for ( int level = qMax( 0, id.zoomLevel() - 1 ); level >= 0; --level ) { if (level > 0 && level < minimumLevel) { continue; } int const deltaLevel = id.zoomLevel() - level; TileId const replacementTileId( id.mapThemeIdHash(), level, id.x() >> deltaLevel, id.y() >> deltaLevel ); QString const fileName = tileFileName( textureData, replacementTileId ); mDebug() << "TileLoader::scaledLowerLevelTile" << "trying" << fileName; QImage toScale = QFile::exists(fileName) ? QImage(fileName) : QImage(); if ( level == 0 && toScale.isNull() ) { mDebug() << "No level zero tile installed in map theme dir. Falling back to a transparent image for now."; QSize tileSize = textureData->tileSize(); Q_ASSERT( !tileSize.isEmpty() ); // assured by textureLayer toScale = QImage( tileSize, QImage::Format_ARGB32_Premultiplied ); toScale.fill( qRgba( 0, 0, 0, 0 ) ); } if ( !toScale.isNull() ) { // which rect to scale? int const restTileX = id.x() % ( 1 << deltaLevel ); int const restTileY = id.y() % ( 1 << deltaLevel ); int const partWidth = qMax(1, toScale.width() >> deltaLevel); int const partHeight = qMax(1, toScale.height() >> deltaLevel); int const startX = restTileX * partWidth; int const startY = restTileY * partHeight; mDebug() << "QImage::copy:" << startX << startY << partWidth << partHeight; QImage const part = toScale.copy( startX, startY, partWidth, partHeight ); mDebug() << "QImage::scaled:" << toScale.size(); return part.scaled( toScale.size() ); } } Q_ASSERT_X( false, "scaled image", "level zero image missing" ); // not reached return QImage(); } GeoDataDocument *TileLoader::openVectorFile(const QString &fileName) const { QList plugins = m_pluginManager->parsingRunnerPlugins(); const QFileInfo fileInfo( fileName ); const QString suffix = fileInfo.suffix().toLower(); const QString completeSuffix = fileInfo.completeSuffix().toLower(); for( const ParseRunnerPlugin *plugin: plugins ) { QStringList const extensions = plugin->fileExtensions(); if ( extensions.contains( suffix ) || extensions.contains( completeSuffix ) ) { ParsingRunner* runner = plugin->newRunner(); QString error; GeoDataDocument* document = runner->parseFile(fileName, UserDocument, error); if (!document && !error.isEmpty()) { - mDebug() << QString("Failed to open vector tile %1: %2").arg(fileName).arg(error); + mDebug() << QString("Failed to open vector tile %1: %2").arg(fileName, error); } delete runner; return document; } } mDebug() << "Unable to open vector tile " << fileName << ": No suitable plugin registered to parse this file format"; return nullptr; } } #include "moc_TileLoader.cpp" diff --git a/src/lib/marble/geodata/data/GeoDataCoordinates.cpp b/src/lib/marble/geodata/data/GeoDataCoordinates.cpp index 310eddf9c..2e7e9032c 100644 --- a/src/lib/marble/geodata/data/GeoDataCoordinates.cpp +++ b/src/lib/marble/geodata/data/GeoDataCoordinates.cpp @@ -1,1177 +1,1177 @@ // // This file is part of the Marble Virtual Globe. // // This program is free software licensed under the GNU LGPL. You can // find a copy of this license in LICENSE.txt in the top directory of // the source code. // // Copyright 2004-2007 Torsten Rahn // Copyright 2007-2008 Inge Wallin // Copyright 2008 Patrick Spendrin // Copyright 2011 Friedrich W. H. Kossebau // Copyright 2011 Bernhard Beschow // Copyright 2015 Alejandro Garcia Montoro // #include "GeoDataCoordinates.h" #include "GeoDataCoordinates_p.h" #include "LonLatParser_p.h" #include #include #include #include "MarbleGlobal.h" #include "MarbleDebug.h" #include "Quaternion.h" namespace Marble { const qreal GeoDataCoordinatesPrivate::sm_semiMajorAxis = 6378137.0; const qreal GeoDataCoordinatesPrivate::sm_semiMinorAxis = 6356752.314; const qreal GeoDataCoordinatesPrivate::sm_eccentricitySquared = 6.69437999013e-03; const qreal GeoDataCoordinatesPrivate::sm_utmScaleFactor = 0.9996; GeoDataCoordinates::Notation GeoDataCoordinates::s_notation = GeoDataCoordinates::DMS; const GeoDataCoordinates GeoDataCoordinates::null = GeoDataCoordinates( 0, 0, 0 ); // don't use default constructor! GeoDataCoordinates::GeoDataCoordinates( qreal _lon, qreal _lat, qreal _alt, GeoDataCoordinates::Unit unit, int _detail ) : d( new GeoDataCoordinatesPrivate( _lon, _lat, _alt, unit, _detail ) ) { d->ref.ref(); } /* simply copy the d pointer * it will be replaced in the detach function instead */ GeoDataCoordinates::GeoDataCoordinates( const GeoDataCoordinates& other ) : d( other.d ) { d->ref.ref(); } /* simply copy null's d pointer * it will be replaced in the detach function */ GeoDataCoordinates::GeoDataCoordinates() : d( null.d ) { d->ref.ref(); } /* * only delete the private d pointer if the number of references is 0 * remember that all copies share the same d pointer! */ GeoDataCoordinates::~GeoDataCoordinates() { delete d->m_q; d->m_q = 0; if (!d->ref.deref()) delete d; #ifdef DEBUG_GEODATA // mDebug() << "delete coordinates"; #endif } bool GeoDataCoordinates::isValid() const { return d != null.d; } /* * if only one copy exists, return * else make a new private d pointer object and assign the values of the current * one to it * at the end, if the number of references thus reaches 0 delete it * this state shouldn't happen, but if it does, we have to clean up behind us. */ void GeoDataCoordinates::detach() { delete d->m_q; d->m_q = 0; if(d->ref.load() == 1) { return; } GeoDataCoordinatesPrivate *new_d = new GeoDataCoordinatesPrivate( *d ); if (!d->ref.deref()) { delete d; } d = new_d; d->ref.ref(); } /* * call detach() at the start of all non-static, non-const functions */ void GeoDataCoordinates::set( qreal _lon, qreal _lat, qreal _alt, GeoDataCoordinates::Unit unit ) { detach(); d->m_altitude = _alt; switch( unit ){ default: case Radian: d->m_lon = _lon; d->m_lat = _lat; break; case Degree: d->m_lon = _lon * DEG2RAD; d->m_lat = _lat * DEG2RAD; break; } } /* * call detach() at the start of all non-static, non-const functions */ void GeoDataCoordinates::setLongitude( qreal _lon, GeoDataCoordinates::Unit unit ) { detach(); switch( unit ){ default: case Radian: d->m_lon = _lon; break; case Degree: d->m_lon = _lon * DEG2RAD; break; } } /* * call detach() at the start of all non-static, non-const functions */ void GeoDataCoordinates::setLatitude( qreal _lat, GeoDataCoordinates::Unit unit ) { detach(); switch( unit ){ case Radian: d->m_lat = _lat; break; case Degree: d->m_lat = _lat * DEG2RAD; break; } } void GeoDataCoordinates::geoCoordinates( qreal& lon, qreal& lat, GeoDataCoordinates::Unit unit ) const { switch ( unit ) { default: case Radian: lon = d->m_lon; lat = d->m_lat; break; case Degree: lon = d->m_lon * RAD2DEG; lat = d->m_lat * RAD2DEG; break; } } void GeoDataCoordinates::geoCoordinates(qreal &lon, qreal &lat) const { lon = d->m_lon; lat = d->m_lat; } void GeoDataCoordinates::geoCoordinates( qreal& lon, qreal& lat, qreal& alt, GeoDataCoordinates::Unit unit ) const { geoCoordinates( lon, lat, unit ); alt = d->m_altitude; } void GeoDataCoordinates::geoCoordinates(qreal &lon, qreal &lat, qreal &alt) const { lon = d->m_lon; lat = d->m_lat; alt = d->m_altitude; } qreal GeoDataCoordinates::longitude( GeoDataCoordinates::Unit unit ) const { switch ( unit ) { default: case Radian: return d->m_lon; case Degree: return d->m_lon * RAD2DEG; } } qreal GeoDataCoordinates::longitude() const { return d->m_lon; } qreal GeoDataCoordinates::latitude( GeoDataCoordinates::Unit unit ) const { switch ( unit ) { default: case Radian: return d->m_lat; case Degree: return d->m_lat * RAD2DEG; } } qreal GeoDataCoordinates::latitude() const { return d->m_lat; } //static GeoDataCoordinates::Notation GeoDataCoordinates::defaultNotation() { return s_notation; } //static void GeoDataCoordinates::setDefaultNotation( GeoDataCoordinates::Notation notation ) { s_notation = notation; } //static qreal GeoDataCoordinates::normalizeLon( qreal lon, GeoDataCoordinates::Unit unit ) { qreal halfCircle; if ( unit == GeoDataCoordinates::Radian ) { halfCircle = M_PI; } else { halfCircle = 180; } if ( lon > halfCircle ) { int cycles = (int)( ( lon + halfCircle ) / ( 2 * halfCircle ) ); return lon - ( cycles * 2 * halfCircle ); } if ( lon < -halfCircle ) { int cycles = (int)( ( lon - halfCircle ) / ( 2 * halfCircle ) ); return lon - ( cycles * 2 * halfCircle ); } return lon; } //static qreal GeoDataCoordinates::normalizeLat( qreal lat, GeoDataCoordinates::Unit unit ) { qreal halfCircle; if ( unit == GeoDataCoordinates::Radian ) { halfCircle = M_PI; } else { halfCircle = 180; } if ( lat > ( halfCircle / 2.0 ) ) { int cycles = (int)( ( lat + halfCircle ) / ( 2 * halfCircle ) ); qreal temp; if( cycles == 0 ) { // pi/2 < lat < pi temp = halfCircle - lat; } else { temp = lat - ( cycles * 2 * halfCircle ); } if ( temp > ( halfCircle / 2.0 ) ) { return ( halfCircle - temp ); } if ( temp < ( -halfCircle / 2.0 ) ) { return ( -halfCircle - temp ); } return temp; } if ( lat < ( -halfCircle / 2.0 ) ) { int cycles = (int)( ( lat - halfCircle ) / ( 2 * halfCircle ) ); qreal temp; if( cycles == 0 ) { temp = -halfCircle - lat; } else { temp = lat - ( cycles * 2 * halfCircle ); } if ( temp > ( +halfCircle / 2.0 ) ) { return ( +halfCircle - temp ); } if ( temp < ( -halfCircle / 2.0 ) ) { return ( -halfCircle - temp ); } return temp; } return lat; } //static void GeoDataCoordinates::normalizeLonLat( qreal &lon, qreal &lat, GeoDataCoordinates::Unit unit ) { qreal halfCircle; if ( unit == GeoDataCoordinates::Radian ) { halfCircle = M_PI; } else { halfCircle = 180; } if ( lon > +halfCircle ) { int cycles = (int)( ( lon + halfCircle ) / ( 2 * halfCircle ) ); lon = lon - ( cycles * 2 * halfCircle ); } if ( lon < -halfCircle ) { int cycles = (int)( ( lon - halfCircle ) / ( 2 * halfCircle ) ); lon = lon - ( cycles * 2 * halfCircle ); } if ( lat > ( +halfCircle / 2.0 ) ) { int cycles = (int)( ( lat + halfCircle ) / ( 2 * halfCircle ) ); qreal temp; if( cycles == 0 ) { // pi/2 < lat < pi temp = halfCircle - lat; } else { temp = lat - ( cycles * 2 * halfCircle ); } if ( temp > ( +halfCircle / 2.0 ) ) { lat = +halfCircle - temp; } if ( temp < ( -halfCircle / 2.0 ) ) { lat = -halfCircle - temp; } lat = temp; if( lon > 0 ) { lon = -halfCircle + lon; } else { lon = halfCircle + lon; } } if ( lat < ( -halfCircle / 2.0 ) ) { int cycles = (int)( ( lat - halfCircle ) / ( 2 * halfCircle ) ); qreal temp; if( cycles == 0 ) { temp = -halfCircle - lat; } else { temp = lat - ( cycles * 2 * halfCircle ); } if ( temp > ( +halfCircle / 2.0 ) ) { lat = +halfCircle - temp; } if ( temp < ( -halfCircle / 2.0 ) ) { lat = -halfCircle - temp; } lat = temp; if( lon > 0 ) { lon = -halfCircle + lon; } else { lon = halfCircle + lon; } } return; } GeoDataCoordinates GeoDataCoordinates::fromString( const QString& string, bool& successful ) { LonLatParser parser; successful = parser.parse(string); if (successful) { return GeoDataCoordinates( parser.lon(), parser.lat(), 0, GeoDataCoordinates::Degree ); } else { return GeoDataCoordinates(); } } QString GeoDataCoordinates::toString() const { return GeoDataCoordinates::toString( s_notation ); } QString GeoDataCoordinates::toString( GeoDataCoordinates::Notation notation, int precision ) const { QString coordString; if( notation == GeoDataCoordinates::UTM ){ int zoneNumber = GeoDataCoordinatesPrivate::lonLatToZone(d->m_lon, d->m_lat); // Handle lack of UTM zone number in the poles const QString zoneString = (zoneNumber > 0) ? QString::number(zoneNumber) : QString(); QString bandString = GeoDataCoordinatesPrivate::lonLatToLatitudeBand(d->m_lon, d->m_lat); QString eastingString = QString::number(GeoDataCoordinatesPrivate::lonLatToEasting(d->m_lon, d->m_lat), 'f', 2); QString northingString = QString::number(GeoDataCoordinatesPrivate::lonLatToNorthing(d->m_lon, d->m_lat), 'f', 2); - return QString("%1%2 %3 m E, %4 m N").arg(zoneString).arg(bandString).arg(eastingString).arg(northingString); + return QString("%1%2 %3 m E, %4 m N").arg(zoneString, bandString, eastingString, northingString); } else{ coordString = lonToString( d->m_lon, notation, Radian, precision ) + QLatin1String(", ") + latToString( d->m_lat, notation, Radian, precision ); } return coordString; } QString GeoDataCoordinates::lonToString( qreal lon, GeoDataCoordinates::Notation notation, GeoDataCoordinates::Unit unit, int precision, char format ) { if( notation == GeoDataCoordinates::UTM ){ /** * @FIXME: UTM needs lon + lat to know zone number and easting * By now, this code returns the zone+easting of the point * (lon, equator), but this can differ a lot at different locations * See bug 347536 https://bugs.kde.org/show_bug.cgi?id=347536 */ qreal lonRad = ( unit == Radian ) ? lon : lon * DEG2RAD; int zoneNumber = GeoDataCoordinatesPrivate::lonLatToZone(lonRad, 0); // Handle lack of UTM zone number in the poles QString result = (zoneNumber > 0) ? QString::number(zoneNumber) : QString(); if(precision > 0){ QString eastingString = QString::number( GeoDataCoordinatesPrivate::lonLatToEasting(lonRad, 0), 'f', 2 ); result += QString(" %1 m E").arg(eastingString); } return result; } QString weString = ( lon < 0 ) ? tr("W") : tr("E"); QString lonString; qreal lonDegF = ( unit == Degree ) ? fabs( lon ) : fabs( (qreal)(lon) * RAD2DEG ); // Take care of -1 case precision = ( precision < 0 ) ? 5 : precision; if ( notation == DMS || notation == DM ) { int lonDeg = (int) lonDegF; qreal lonMinF = 60 * (lonDegF - lonDeg); int lonMin = (int) lonMinF; qreal lonSecF = 60 * (lonMinF - lonMin); int lonSec = (int) lonSecF; // Adjustment for fuzziness (like 49.999999999999999999999) if ( precision == 0 ) { lonDeg = qRound( lonDegF ); } else if ( precision <= 2 ) { lonMin = qRound( lonMinF ); } else if ( precision <= 4 && notation == DMS ) { lonSec = qRound( lonSecF ); } else { if ( notation == DMS ) { lonSec = lonSecF = qRound( lonSecF * qPow( 10, precision - 4 ) ) / qPow( 10, precision - 4 ); } else { lonMin = lonMinF = qRound( lonMinF * qPow( 10, precision - 2 ) ) / qPow( 10, precision - 2 ); } } if (lonSec > 59 && notation == DMS ) { lonSec = lonSecF = 0; lonMin = lonMinF = lonMinF + 1; } if (lonMin > 59) { lonMin = lonMinF = 0; lonDeg = lonDegF = lonDegF + 1; } // Evaluate the string lonString = QString::fromUtf8("%1\xc2\xb0").arg(lonDeg, 3, 10, QLatin1Char(' ')); if ( precision == 0 ) { return lonString + weString; } if ( notation == DMS || precision < 3 ) { lonString += QString(" %2\'").arg(lonMin, 2, 10, QLatin1Char('0')); } if ( precision < 3 ) { return lonString + weString; } if ( notation == DMS ) { // Includes -1 case! if ( precision < 5 ) { lonString += QString(" %3\"").arg(lonSec, 2, 'f', 0, QLatin1Char('0')); return lonString + weString; } lonString += QString(" %L3\"").arg(lonSecF, precision - 1, 'f', precision - 4, QLatin1Char('0')); } else { lonString += QString(" %L3'").arg(lonMinF, precision + 1, 'f', precision - 2, QLatin1Char('0')); } } else if ( notation == GeoDataCoordinates::Decimal ) { lonString = QString::fromUtf8("%L1\xc2\xb0").arg(lonDegF, 4 + precision, format, precision, QLatin1Char(' ')); } else if ( notation == GeoDataCoordinates::Astro ) { if (lon < 0) { lon += ( unit == Degree ) ? 360 : 2 * M_PI; } qreal lonHourF = ( unit == Degree ) ? fabs( lon/15.0 ) : fabs( (qreal)(lon/15.0) * RAD2DEG ); int lonHour = (int) lonHourF; qreal lonMinF = 60 * (lonHourF - lonHour); int lonMin = (int) lonMinF; qreal lonSecF = 60 * (lonMinF - lonMin); int lonSec = (int) lonSecF; // Adjustment for fuzziness (like 49.999999999999999999999) if ( precision == 0 ) { lonHour = qRound( lonHourF ); } else if ( precision <= 2 ) { lonMin = qRound( lonMinF ); } else if ( precision <= 4 ) { lonSec = qRound( lonSecF ); } else { lonSec = lonSecF = qRound( lonSecF * qPow( 10, precision - 4 ) ) / qPow( 10, precision - 4 ); } if (lonSec > 59 ) { lonSec = lonSecF = 0; lonMin = lonMinF = lonMinF + 1; } if (lonMin > 59) { lonMin = lonMinF = 0; lonHour = lonHourF = lonHourF + 1; } // Evaluate the string lonString = QString::fromUtf8("%1h").arg(lonHour, 3, 10, QLatin1Char(' ')); if ( precision == 0 ) { return lonString; } lonString += QString(" %2\'").arg(lonMin, 2, 10, QLatin1Char('0')); if ( precision < 3 ) { return lonString; } // Includes -1 case! if ( precision < 5 ) { lonString += QString(" %3\"").arg(lonSec, 2, 'f', 0, QLatin1Char('0')); return lonString; } lonString += QString(" %L3\"").arg(lonSecF, precision - 1, 'f', precision - 4, QLatin1Char('0')); return lonString; } return lonString + weString; } QString GeoDataCoordinates::lonToString() const { return GeoDataCoordinates::lonToString( d->m_lon , s_notation ); } QString GeoDataCoordinates::latToString( qreal lat, GeoDataCoordinates::Notation notation, GeoDataCoordinates::Unit unit, int precision, char format ) { if( notation == GeoDataCoordinates::UTM ){ /** * @FIXME: UTM needs lon + lat to know latitude band and northing * By now, this code returns the band+northing of the point * (meridian, lat), but this can differ a lot at different locations * See bug 347536 https://bugs.kde.org/show_bug.cgi?id=347536 */ qreal latRad = ( unit == Radian ) ? lat : lat * DEG2RAD; QString result = GeoDataCoordinatesPrivate::lonLatToLatitudeBand(0, latRad); if ( precision > 0 ){ QString northingString = QString::number( GeoDataCoordinatesPrivate::lonLatToNorthing(0, latRad), 'f', 2 ); result += QString(" %1 m N").arg(northingString); } return result; } QString pmString; QString nsString; if (notation == GeoDataCoordinates::Astro){ pmString = ( lat > 0 ) ? "+" : "-"; } else { nsString = ( lat > 0 ) ? tr("N") : tr("S"); } QString latString; qreal latDegF = ( unit == Degree ) ? fabs( lat ) : fabs( (qreal)(lat) * RAD2DEG ); // Take care of -1 case precision = ( precision < 0 ) ? 5 : precision; if ( notation == DMS || notation == DM || notation == Astro) { int latDeg = (int) latDegF; qreal latMinF = 60 * (latDegF - latDeg); int latMin = (int) latMinF; qreal latSecF = 60 * (latMinF - latMin); int latSec = (int) latSecF; // Adjustment for fuzziness (like 49.999999999999999999999) if ( precision == 0 ) { latDeg = qRound( latDegF ); } else if ( precision <= 2 ) { latMin = qRound( latMinF ); } else if ( precision <= 4 && notation == DMS ) { latSec = qRound( latSecF ); } else { if ( notation == DMS || notation == Astro ) { latSec = latSecF = qRound( latSecF * qPow( 10, precision - 4 ) ) / qPow( 10, precision - 4 ); } else { latMin = latMinF = qRound( latMinF * qPow( 10, precision - 2 ) ) / qPow( 10, precision - 2 ); } } if (latSec > 59 && ( notation == DMS || notation == Astro )) { latSecF = 0; latSec = latSecF; latMin = latMin + 1; } if (latMin > 59) { latMinF = 0; latMin = latMinF; latDeg = latDeg + 1; } // Evaluate the string latString = QString::fromUtf8("%1\xc2\xb0").arg(latDeg, 3, 10, QLatin1Char(' ')); if ( precision == 0 ) { return pmString + latString + nsString; } if ( notation == DMS || notation == Astro || precision < 3 ) { latString += QString(" %2\'").arg(latMin, 2, 10, QLatin1Char('0')); } if ( precision < 3 ) { return pmString + latString + nsString; } if ( notation == DMS || notation == Astro ) { // Includes -1 case! if ( precision < 5 ) { latString += QString(" %3\"").arg(latSec, 2, 'f', 0, QLatin1Char('0')); return latString + nsString; } latString += QString(" %L3\"").arg(latSecF, precision - 1, 'f', precision - 4, QLatin1Char('0')); } else { latString += QString(" %L3'").arg(latMinF, precision + 1, 'f', precision - 2, QLatin1Char('0')); } } else // notation = GeoDataCoordinates::Decimal { latString = QString::fromUtf8("%L1\xc2\xb0").arg(latDegF, 4 + precision, format, precision, QLatin1Char(' ')); } return pmString + latString + nsString; } QString GeoDataCoordinates::latToString() const { return GeoDataCoordinates::latToString( d->m_lat, s_notation ); } bool GeoDataCoordinates::operator==( const GeoDataCoordinates &rhs ) const { return *d == *rhs.d; } bool GeoDataCoordinates::operator!=( const GeoDataCoordinates &rhs ) const { return *d != *rhs.d; } void GeoDataCoordinates::setAltitude( const qreal altitude ) { detach(); d->m_altitude = altitude; } qreal GeoDataCoordinates::altitude() const { return d->m_altitude; } int GeoDataCoordinates::utmZone() const{ return GeoDataCoordinatesPrivate::lonLatToZone(d->m_lon, d->m_lat); } qreal GeoDataCoordinates::utmEasting() const{ return GeoDataCoordinatesPrivate::lonLatToEasting(d->m_lon, d->m_lat); } QString GeoDataCoordinates::utmLatitudeBand() const{ return GeoDataCoordinatesPrivate::lonLatToLatitudeBand(d->m_lon, d->m_lat); } qreal GeoDataCoordinates::utmNorthing() const{ return GeoDataCoordinatesPrivate::lonLatToNorthing(d->m_lon, d->m_lat); } quint8 GeoDataCoordinates::detail() const { return d->m_detail; } void GeoDataCoordinates::setDetail(quint8 detail) { detach(); d->m_detail = detail; } GeoDataCoordinates GeoDataCoordinates::rotateAround( const GeoDataCoordinates &axis, qreal angle, Unit unit ) const { const Quaternion quatAxis = Quaternion::fromEuler( -axis.latitude() , axis.longitude(), 0 ); const Quaternion rotationAmount = Quaternion::fromEuler( 0, 0, unit == Radian ? angle : angle * DEG2RAD ); const Quaternion resultAxis = quatAxis * rotationAmount * quatAxis.inverse(); Quaternion rotatedQuat = quaternion(); rotatedQuat.rotateAroundAxis(resultAxis); qreal rotatedLon, rotatedLat; rotatedQuat.getSpherical(rotatedLon, rotatedLat); return GeoDataCoordinates(rotatedLon, rotatedLat, altitude()); } qreal GeoDataCoordinates::bearing( const GeoDataCoordinates &other, Unit unit, BearingType type ) const { if ( type == FinalBearing ) { double const offset = unit == Degree ? 180.0 : M_PI; return offset + other.bearing( *this, unit, InitialBearing ); } qreal const delta = other.d->m_lon - d->m_lon; double const bearing = atan2( sin ( delta ) * cos ( other.d->m_lat ), cos( d->m_lat ) * sin( other.d->m_lat ) - sin( d->m_lat ) * cos( other.d->m_lat ) * cos ( delta ) ); return unit == Radian ? bearing : bearing * RAD2DEG; } GeoDataCoordinates GeoDataCoordinates::moveByBearing( qreal bearing, qreal distance ) const { qreal newLat = asin( sin(d->m_lat) * cos(distance) + cos(d->m_lat) * sin(distance) * cos(bearing) ); qreal newLon = d->m_lon + atan2( sin(bearing) * sin(distance) * cos(d->m_lat), cos(distance) - sin(d->m_lat) * sin(newLat) ); return GeoDataCoordinates( newLon, newLat ); } const Quaternion& GeoDataCoordinates::quaternion() const { if (d->m_q == 0) { d->m_q = new Quaternion(Quaternion::fromSpherical( d->m_lon , d->m_lat )); } return *d->m_q; } GeoDataCoordinates GeoDataCoordinates::interpolate( const GeoDataCoordinates &target, double t_ ) const { double const t = qBound( 0.0, t_, 1.0 ); Quaternion const quat = Quaternion::slerp( quaternion(), target.quaternion(), t ); qreal lon, lat; quat.getSpherical( lon, lat ); double const alt = (1.0-t) * d->m_altitude + t * target.d->m_altitude; return GeoDataCoordinates( lon, lat, alt ); } GeoDataCoordinates GeoDataCoordinates::interpolate( const GeoDataCoordinates &before, const GeoDataCoordinates &target, const GeoDataCoordinates &after, double t_ ) const { double const t = qBound( 0.0, t_, 1.0 ); Quaternion const b1 = GeoDataCoordinatesPrivate::basePoint( before.quaternion(), quaternion(), target.quaternion() ); Quaternion const a2 = GeoDataCoordinatesPrivate::basePoint( quaternion(), target.quaternion(), after.quaternion() ); Quaternion const a = Quaternion::slerp( quaternion(), target.quaternion(), t ); Quaternion const b = Quaternion::slerp( b1, a2, t ); Quaternion c = Quaternion::slerp( a, b, 2 * t * (1.0-t) ); qreal lon, lat; c.getSpherical( lon, lat ); // @todo spline interpolation of altitude? double const alt = (1.0-t) * d->m_altitude + t * target.d->m_altitude; return GeoDataCoordinates( lon, lat, alt ); } bool GeoDataCoordinates::isPole( Pole pole ) const { // Evaluate the most likely case first: // The case where we haven't hit the pole and where our latitude is normalized // to the range of 90 deg S ... 90 deg N if ( fabs( (qreal) 2.0 * d->m_lat ) < M_PI ) { return false; } else { if ( fabs( (qreal) 2.0 * d->m_lat ) == M_PI ) { // Ok, we have hit a pole. Now let's check whether it's the one we've asked for: if ( pole == AnyPole ){ return true; } else { if ( pole == NorthPole && 2.0 * d->m_lat == +M_PI ) { return true; } if ( pole == SouthPole && 2.0 * d->m_lat == -M_PI ) { return true; } return false; } } // else { // FIXME: Should we just normalize latitude and longitude and be done? // While this might work well for persistent data it would create some // possible overhead for temporary data, so this needs careful thinking. mDebug() << "GeoDataCoordinates not normalized!"; // Only as a last resort we cover the unlikely case where // the latitude is not normalized to the range of // 90 deg S ... 90 deg N if ( fabs( (qreal) 2.0 * normalizeLat( d->m_lat ) ) < M_PI ) { return false; } else { // Ok, we have hit a pole. Now let's check whether it's the one we've asked for: if ( pole == AnyPole ){ return true; } else { if ( pole == NorthPole && 2.0 * d->m_lat == +M_PI ) { return true; } if ( pole == SouthPole && 2.0 * d->m_lat == -M_PI ) { return true; } return false; } } } } } GeoDataCoordinates& GeoDataCoordinates::operator=( const GeoDataCoordinates &other ) { qAtomicAssign(d, other.d); return *this; } void GeoDataCoordinates::pack( QDataStream& stream ) const { stream << d->m_lon; stream << d->m_lat; stream << d->m_altitude; } void GeoDataCoordinates::unpack( QDataStream& stream ) { // call detach even though it shouldn't be needed - one never knows detach(); stream >> d->m_lon; stream >> d->m_lat; stream >> d->m_altitude; } Quaternion GeoDataCoordinatesPrivate::basePoint( const Quaternion &q1, const Quaternion &q2, const Quaternion &q3 ) { Quaternion const a = (q2.inverse() * q3).log(); Quaternion const b = (q2.inverse() * q1).log(); return q2 * ((a+b)*-0.25).exp(); } qreal GeoDataCoordinatesPrivate::arcLengthOfMeridian( qreal phi ) { // Precalculate n qreal const n = (GeoDataCoordinatesPrivate::sm_semiMajorAxis - GeoDataCoordinatesPrivate::sm_semiMinorAxis) / (GeoDataCoordinatesPrivate::sm_semiMajorAxis + GeoDataCoordinatesPrivate::sm_semiMinorAxis); // Precalculate alpha qreal const alpha = ( (GeoDataCoordinatesPrivate::sm_semiMajorAxis + GeoDataCoordinatesPrivate::sm_semiMinorAxis) / 2.0) * (1.0 + (qPow (n, 2.0) / 4.0) + (qPow (n, 4.0) / 64.0) ); // Precalculate beta qreal const beta = (-3.0 * n / 2.0) + (9.0 * qPow (n, 3.0) / 16.0) + (-3.0 * qPow (n, 5.0) / 32.0); // Precalculate gamma qreal const gamma = (15.0 * qPow (n, 2.0) / 16.0) + (-15.0 * qPow (n, 4.0) / 32.0); // Precalculate delta qreal const delta = (-35.0 * qPow (n, 3.0) / 48.0) + (105.0 * qPow (n, 5.0) / 256.0); // Precalculate epsilon qreal const epsilon = (315.0 * qPow (n, 4.0) / 512.0); // Now calculate the sum of the series and return qreal const result = alpha * (phi + (beta * qSin (2.0 * phi)) + (gamma * qSin (4.0 * phi)) + (delta * qSin (6.0 * phi)) + (epsilon * qSin (8.0 * phi))); return result; } qreal GeoDataCoordinatesPrivate::centralMeridianUTM( qreal zone ) { return DEG2RAD*(-183.0 + (zone * 6.0)); } qreal GeoDataCoordinatesPrivate::footpointLatitude( qreal northing ) { // Precalculate n (Eq. 10.18) qreal const n = (GeoDataCoordinatesPrivate::sm_semiMajorAxis - GeoDataCoordinatesPrivate::sm_semiMinorAxis) / (GeoDataCoordinatesPrivate::sm_semiMajorAxis + GeoDataCoordinatesPrivate::sm_semiMinorAxis); // Precalculate alpha (Eq. 10.22) // (Same as alpha in Eq. 10.17) qreal const alpha = ((GeoDataCoordinatesPrivate::sm_semiMajorAxis + GeoDataCoordinatesPrivate::sm_semiMinorAxis) / 2.0) * (1 + (qPow (n, 2.0) / 4) + (qPow (n, 4.0) / 64)); // Precalculate y (Eq. 10.23) qreal const y = northing / alpha; // Precalculate beta (Eq. 10.22) qreal const beta = (3.0 * n / 2.0) + (-27.0 * qPow (n, 3.0) / 32.0) + (269.0 * qPow (n, 5.0) / 512.0); // Precalculate gamma (Eq. 10.22) qreal const gamma = (21.0 * qPow (n, 2.0) / 16.0) + (-55.0 * qPow (n, 4.0) / 32.0); // Precalculate delta (Eq. 10.22) qreal const delta = (151.0 * qPow (n, 3.0) / 96.0) + (-417.0 * qPow (n, 5.0) / 128.0); // Precalculate epsilon (Eq. 10.22) qreal const epsilon = (1097.0 * qPow (n, 4.0) / 512.0); // Now calculate the sum of the series (Eq. 10.21) qreal const result = y + (beta * qSin (2.0 * y)) + (gamma * qSin (4.0 * y)) + (delta * qSin (6.0 * y)) + (epsilon * qSin (8.0 * y)); return result; } QPointF GeoDataCoordinatesPrivate::mapLonLatToXY( qreal lambda, qreal phi, qreal lambda0 ) { // Equation (10.15) // Precalculate second numerical eccentricity qreal const ep2 = (qPow (GeoDataCoordinatesPrivate::sm_semiMajorAxis, 2.0) - qPow (GeoDataCoordinatesPrivate::sm_semiMinorAxis, 2.0)) / qPow (GeoDataCoordinatesPrivate::sm_semiMinorAxis, 2.0); // Precalculate the square of nu, just an auxiliar quantity qreal const nu2 = ep2 * qPow (qCos(phi), 2.0); // Precalculate the radius of curvature in prime vertical qreal const N = qPow (GeoDataCoordinatesPrivate::sm_semiMajorAxis, 2.0) / (GeoDataCoordinatesPrivate::sm_semiMinorAxis * qSqrt (1 + nu2)); // Precalculate the tangent of phi and its square qreal const t = qTan (phi); qreal const t2 = t * t; // Precalculate longitude difference qreal const l = lambda - lambda0; /* * Precalculate coefficients for l**n in the equations below * so a normal human being can read the expressions for easting * and northing * -- l**1 and l**2 have coefficients of 1.0 * * The actual used coefficients starts at coef[1], just to * follow the meaningful nomenclature in equation 10.15 * (coef[n] corresponds to qPow(l,n) factor) */ QVector coef(9); coef[0] = coef[1] = coef[2] = 1.0; coef[3] = 1.0 - t2 + nu2; coef[4] = 5.0 - t2 + 9 * nu2 + 4.0 * (nu2 * nu2); coef[5] = 5.0 - 18.0 * t2 + (t2 * t2) + 14.0 * nu2 - 58.0 * t2 * nu2; coef[6] = 61.0 - 58.0 * t2 + (t2 * t2) + 270.0 * nu2 - 330.0 * t2 * nu2; coef[7] = 61.0 - 479.0 * t2 + 179.0 * (t2 * t2) - (t2 * t2 * t2); coef[8] = 1385.0 - 3111.0 * t2 + 543.0 * (t2 * t2) - (t2 * t2 * t2); // Calculate easting (x) qreal easting = N * qCos(phi) * coef[1] * l + (N / 6.0 * qPow (qCos(phi), 3.0) * coef[3] * qPow (l, 3.0)) + (N / 120.0 * qPow (qCos(phi), 5.0) * coef[5] * qPow (l, 5.0)) + (N / 5040.0 * qPow (qCos(phi), 7.0) * coef[7] * qPow (l, 7.0)); // Calculate northing (y) qreal northing = arcLengthOfMeridian (phi) + (t / 2.0 * N * qPow (qCos(phi), 2.0) * coef[2] * qPow (l, 2.0)) + (t / 24.0 * N * qPow (qCos(phi), 4.0) * coef[4] * qPow (l, 4.0)) + (t / 720.0 * N * qPow (qCos(phi), 6.0) * coef[6] * qPow (l, 6.0)) + (t / 40320.0 * N * qPow (qCos(phi), 8.0) * coef[8] * qPow (l, 8.0)); return QPointF(easting, northing); } int GeoDataCoordinatesPrivate::lonLatToZone( qreal lon, qreal lat ){ // Converts lon and lat to degrees qreal lonDeg = lon * RAD2DEG; qreal latDeg = lat * RAD2DEG; /* Round the value of the longitude when the distance to the nearest integer * is less than 0.0000001. This avoids fuzzy values such as -114.0000000001, which * can produce a misbehaviour when calculating the zone associated at the borders * of the zone intervals (for example, the interval [-114, -108[ is associated with * zone number 12; if the following rounding is not done, the value returned by * lonLatToZone(114,0) is 11 instead of 12, as the function actually receives * -114.0000000001, which is in the interval [-120,-114[, associated to zone 11 */ qreal precision = 0.0000001; if ( qAbs(lonDeg - qFloor(lonDeg)) < precision || qAbs(lonDeg - qCeil(lonDeg)) < precision ){ lonDeg = qRound(lonDeg); } // There is no numbering associated to the poles, special value 0 is returned. if ( latDeg < -80 || latDeg > 84 ) { return 0; } // Obtains the zone number handling all the so called "exceptions" // See problem: http://en.wikipedia.org/wiki/Universal_Transverse_Mercator_coordinate_system#Exceptions // See solution: http://gis.stackexchange.com/questions/13291/computing-utm-zone-from-lat-long-point // General int zoneNumber = static_cast( (lonDeg+180) / 6.0 ) + 1; // Southwest Norway if ( latDeg >= 56 && latDeg < 64 && lonDeg >= 3 && lonDeg < 12 ) { zoneNumber = 32; } // Svalbard if ( latDeg >= 72 && latDeg < 84 ) { if ( lonDeg >= 0 && lonDeg < 9 ) { zoneNumber = 31; } else if ( lonDeg >= 9 && lonDeg < 21 ) { zoneNumber = 33; } else if ( lonDeg >= 21 && lonDeg < 33 ) { zoneNumber = 35; } else if ( lonDeg >= 33 && lonDeg < 42 ) { zoneNumber = 37; } } return zoneNumber; } qreal GeoDataCoordinatesPrivate::lonLatToEasting( qreal lon, qreal lat ){ int zoneNumber = lonLatToZone( lon, lat ); if ( zoneNumber == 0 ){ qreal lonDeg = lon * RAD2DEG; zoneNumber = static_cast( (lonDeg+180) / 6.0 ) + 1; } QPointF coordinates = GeoDataCoordinatesPrivate::mapLonLatToXY( lon, lat, GeoDataCoordinatesPrivate::centralMeridianUTM(zoneNumber) ); // Adjust easting and northing for UTM system qreal easting = coordinates.x() * GeoDataCoordinatesPrivate::sm_utmScaleFactor + 500000.0; return easting; } QString GeoDataCoordinatesPrivate::lonLatToLatitudeBand( qreal lon, qreal lat ){ // Obtains the latitude bands handling all the so called "exceptions" // Converts lon and lat to degrees qreal lonDeg = lon * RAD2DEG; qreal latDeg = lat * RAD2DEG; // Regular latitude bands between 80 S and 80 N (that is, between 10 and 170 in the [0,180] interval) int bandLetterIndex = 24; //Avoids "may be used uninitialized" warning if ( latDeg < -80 ) { // South pole (A for zones 1-30, B for zones 31-60) bandLetterIndex = ( (lonDeg+180) < 6*31 ) ? 0 : 1; } else if ( latDeg >= -80 && latDeg <= 80 ) { // General (+2 because the general lettering starts in C) bandLetterIndex = static_cast( (latDeg+80.0) / 8.0 ) + 2; } else if ( latDeg >= 80 && latDeg < 84 ) { // Band X is extended 4 more degrees bandLetterIndex = 21; } else if ( latDeg >= 84 ) { // North pole (Y for zones 1-30, Z for zones 31-60) bandLetterIndex = ((lonDeg+180) < 6*31) ? 22 : 23; } return QString( "ABCDEFGHJKLMNPQRSTUVWXYZ?" ).at( bandLetterIndex ); } qreal GeoDataCoordinatesPrivate::lonLatToNorthing( qreal lon, qreal lat ){ int zoneNumber = lonLatToZone( lon, lat ); if ( zoneNumber == 0 ){ qreal lonDeg = lon * RAD2DEG; zoneNumber = static_cast( (lonDeg+180) / 6.0 ) + 1; } QPointF coordinates = GeoDataCoordinatesPrivate::mapLonLatToXY( lon, lat, GeoDataCoordinatesPrivate::centralMeridianUTM(zoneNumber) ); qreal northing = coordinates.y() * GeoDataCoordinatesPrivate::sm_utmScaleFactor; if ( northing < 0.0 ) { northing += 10000000.0; } return northing; } uint qHash(const GeoDataCoordinates &coordinates) { QString lon, lat, alt; lon.setNum(coordinates.longitude(), 'f', 10); lat.setNum(coordinates.latitude(), 'f', 10); alt.setNum(coordinates.altitude(), 'f', 3); return qHash(lon % lat % alt); } } diff --git a/src/lib/marble/geodata/graphicsitem/GeoLineStringGraphicsItem.cpp b/src/lib/marble/geodata/graphicsitem/GeoLineStringGraphicsItem.cpp index 21bc599a6..c2b600360 100644 --- a/src/lib/marble/geodata/graphicsitem/GeoLineStringGraphicsItem.cpp +++ b/src/lib/marble/geodata/graphicsitem/GeoLineStringGraphicsItem.cpp @@ -1,512 +1,512 @@ // // This file is part of the Marble Virtual Globe. // // This program is free software licensed under the GNU LGPL. You can // find a copy of this license in LICENSE.txt in the top directory of // the source code. // // Copyright 2009 Andrew Manson // #include "GeoLineStringGraphicsItem.h" #include "GeoDataLineString.h" #include "GeoDataLineStyle.h" #include "GeoDataLabelStyle.h" #include "GeoDataPlacemark.h" #include "GeoDataPolyStyle.h" #include "GeoPainter.h" #include "StyleBuilder.h" #include "ViewportParams.h" #include "GeoDataStyle.h" #include "GeoDataColorStyle.h" #include "MarbleDebug.h" #include "MarbleMath.h" #include "OsmPlacemarkData.h" #include #include namespace Marble { const GeoDataStyle *GeoLineStringGraphicsItem::s_previousStyle = 0; bool GeoLineStringGraphicsItem::s_paintInline = true; bool GeoLineStringGraphicsItem::s_paintOutline = true; GeoLineStringGraphicsItem::GeoLineStringGraphicsItem(const GeoDataPlacemark *placemark, const GeoDataLineString *lineString) : GeoGraphicsItem(placemark), m_lineString(lineString), m_renderLineString(lineString), m_renderLabel(false), m_penWidth(0.0), m_name(placemark->name()) { QString const category = StyleBuilder::visualCategoryName(placemark->visualCategory()); QStringList paintLayers; paintLayers << QLatin1String("LineString/") + category + QLatin1String("/outline"); paintLayers << QLatin1String("LineString/") + category + QLatin1String("/inline"); if (!m_name.isEmpty()) { paintLayers << QLatin1String("LineString/") + category + QLatin1String("/label"); } setPaintLayers(paintLayers); } GeoLineStringGraphicsItem::~GeoLineStringGraphicsItem() { qDeleteAll(m_cachedPolygons); } void GeoLineStringGraphicsItem::setLineString( const GeoDataLineString* lineString ) { m_lineString = lineString; m_renderLineString = lineString; } const GeoDataLineString *GeoLineStringGraphicsItem::lineString() const { return m_lineString; } GeoDataLineString GeoLineStringGraphicsItem::merge(const QVector &lineStrings_) { if (lineStrings_.isEmpty()) { return GeoDataLineString(); } Q_ASSERT(!lineStrings_.isEmpty()); auto lineStrings = lineStrings_; GeoDataLineString result = *lineStrings.first(); lineStrings.pop_front(); for (bool matched = true; matched && !lineStrings.isEmpty();) { matched = false; for (auto lineString: lineStrings) { if (canMerge(result.first(), lineString->first())) { result.remove(0); result.reverse(); result << *lineString; lineStrings.removeOne(lineString); matched = true; break; } else if (canMerge(result.last(), lineString->first())) { result.remove(result.size()-1); result << *lineString; lineStrings.removeOne(lineString); matched = true; break; } else if (canMerge(result.first(), lineString->last())) { GeoDataLineString behind = result; result = *lineString; behind.remove(0); result << behind; lineStrings.removeOne(lineString); matched = true; break; } else if (canMerge(result.last(), lineString->last())) { GeoDataLineString behind = *lineString; behind.reverse(); behind.remove(0); result << behind; lineStrings.removeOne(lineString); matched = true; break; } } if (!matched) { return GeoDataLineString(); } } return lineStrings.isEmpty() ? result : GeoDataLineString(); } void GeoLineStringGraphicsItem::setMergedLineString(const GeoDataLineString &mergedLineString) { m_mergedLineString = mergedLineString; m_renderLineString = mergedLineString.isEmpty() ? m_lineString : &m_mergedLineString; } const GeoDataLatLonAltBox& GeoLineStringGraphicsItem::latLonAltBox() const { return m_renderLineString->latLonAltBox(); } void GeoLineStringGraphicsItem::paint(GeoPainter* painter, const ViewportParams* viewport , const QString &layer, int tileLevel) { setRenderContext(RenderContext(tileLevel)); if (layer.endsWith(QLatin1String("/outline"))) { qDeleteAll(m_cachedPolygons); m_cachedPolygons.clear(); m_cachedRegion = QRegion(); painter->polygonsFromLineString(*m_renderLineString, m_cachedPolygons); if (m_cachedPolygons.empty()) { return; } if (painter->mapQuality() == HighQuality || painter->mapQuality() == PrintQuality) { paintOutline(painter, viewport); } } else if (layer.endsWith(QLatin1String("/inline"))) { if (m_cachedPolygons.empty()) { return; } paintInline(painter, viewport); } else if (layer.endsWith(QLatin1String("/label"))) { if (!m_cachedPolygons.empty()) { if (m_renderLabel) { paintLabel(painter, viewport); } } } else { qDeleteAll(m_cachedPolygons); m_cachedPolygons.clear(); m_cachedRegion = QRegion(); painter->polygonsFromLineString(*m_renderLineString, m_cachedPolygons); if (m_cachedPolygons.empty()) { return; } for(const QPolygonF* itPolygon: m_cachedPolygons) { painter->drawPolyline(*itPolygon); } } } bool GeoLineStringGraphicsItem::contains(const QPoint &screenPosition, const ViewportParams *) const { if (m_penWidth <= 0.0) { return false; } if (m_cachedRegion.isNull()) { QPainterPath painterPath; for (auto polygon: m_cachedPolygons) { painterPath.addPolygon(*polygon); } QPainterPathStroker stroker; qreal const margin = 6.0; stroker.setWidth(m_penWidth + margin); QPainterPath strokePath = stroker.createStroke(painterPath); m_cachedRegion = QRegion(strokePath.toFillPolygon().toPolygon(), Qt::WindingFill); } return m_cachedRegion.contains(screenPosition); } void GeoLineStringGraphicsItem::handleRelationUpdate(const QVector &relations) { QHash names; for (auto relation: relations) { auto const ref = relation->osmData().tagValue(QStringLiteral("ref")); if (relation->isVisible() && !ref.isEmpty()) { names[relation->relationType()] << ref; } } if (names.isEmpty()) { m_name = feature()->name(); } else { QStringList namesList; for (auto iter = names.begin(); iter != names.end(); ++iter) { QString value; switch (iter.key()) { case GeoDataRelation::UnknownType: case GeoDataRelation::RouteRoad: break; case GeoDataRelation::RouteDetour: value = tr("Detour"); break; case GeoDataRelation::RouteFerry: value = tr("Ferry Route"); break; case GeoDataRelation::RouteTrain: value = tr("Train"); break; case GeoDataRelation::RouteSubway: value = tr("Subway"); break; case GeoDataRelation::RouteTram: value = tr("Tram"); break; case GeoDataRelation::RouteBus: value = tr("Bus"); break; case GeoDataRelation::RouteTrolleyBus: value = tr("Trolley Bus"); break; case GeoDataRelation::RouteBicycle: value = tr("Bicycle Route"); break; case GeoDataRelation::RouteMountainbike: value = tr("Mountainbike Route"); break; case GeoDataRelation::RouteFoot: value = tr("Walking Route"); break; case GeoDataRelation::RouteHiking: value = tr("Hiking Route"); break; case GeoDataRelation::RouteHorse: value = tr("Bridleway"); break; case GeoDataRelation::RouteInlineSkates: value = tr("Inline Skates Route"); break; case GeoDataRelation::RouteSkiDownhill: value = tr("Downhill Piste"); break; case GeoDataRelation::RouteSkiNordic: value = tr("Nordic Ski Trail"); break; case GeoDataRelation::RouteSkitour: value = tr("Skitour"); break; case GeoDataRelation::RouteSled: value = tr("Sled Trail"); break; } QStringList &references = iter.value(); std::sort(references.begin(), references.end()); auto const last = std::unique(references.begin(), references.end()); references.erase(last, references.end()); auto const routes = references.join(", "); - namesList << (value.isEmpty() ? routes : QStringLiteral("%1 %2").arg(value).arg(routes)); + namesList << (value.isEmpty() ? routes : QStringLiteral("%1 %2").arg(value, routes)); } auto const allRoutes = namesList.join(QStringLiteral("; ")); if (feature()->name().isEmpty()) { m_name = allRoutes; } else { m_name = QStringLiteral("%1 (%2)").arg(feature()->name(), allRoutes); } } } void GeoLineStringGraphicsItem::paintInline(GeoPainter* painter, const ViewportParams* viewport) { if ( ( !viewport->resolves( m_renderLineString->latLonAltBox(), 2) ) ) { return; } if (s_previousStyle != style().data()) { s_paintInline = configurePainterForLine(painter, viewport, false); } s_previousStyle = style().data(); if (s_paintInline) { m_renderLabel = painter->pen().widthF() >= 6.0f; m_penWidth = painter->pen().widthF(); for(const QPolygonF* itPolygon: m_cachedPolygons) { painter->drawPolyline(*itPolygon); } } } void GeoLineStringGraphicsItem::paintOutline(GeoPainter *painter, const ViewportParams *viewport) const { if ( ( !viewport->resolves( m_renderLineString->latLonAltBox(), 2) ) ) { return; } if (s_previousStyle != style().data()) { s_paintOutline = configurePainterForLine(painter, viewport, true); } s_previousStyle = style().data(); if (s_paintOutline) { for(const QPolygonF* itPolygon: m_cachedPolygons) { painter->drawPolyline(*itPolygon); } } } void GeoLineStringGraphicsItem::paintLabel(GeoPainter *painter, const ViewportParams *viewport) const { if ( ( !viewport->resolves( m_renderLineString->latLonAltBox(), 2) ) ) { return; } LabelPositionFlags labelPositionFlags = NoLabel; bool isValid = configurePainterForLabel(painter, viewport, labelPositionFlags); if (isValid) { GeoDataStyle::ConstPtr style = this->style(); // Activate the lines below to paint a label background which // prevents antialiasing overpainting glitches, but leads to // other glitches. //QColor const color = style->polyStyle().paintedColor(); //painter->setBackground(QBrush(color)); //painter->setBackgroundMode(Qt::OpaqueMode); const GeoDataLabelStyle& labelStyle = style->labelStyle(); painter->drawLabelsForPolygons(m_cachedPolygons, m_name, FollowLine, labelStyle.paintedColor()); } } bool GeoLineStringGraphicsItem::configurePainterForLine(GeoPainter *painter, const ViewportParams *viewport, const bool isOutline) const { QPen currentPen = painter->pen(); GeoDataStyle::ConstPtr style = this->style(); if (!style) { painter->setPen( QPen() ); painter->setBackground(QBrush(Qt::transparent)); painter->setBackgroundMode(Qt::TransparentMode); } else { if (isOutline && !style->polyStyle().outline()) { return false; } const GeoDataLineStyle& lineStyle = style->lineStyle(); // To save performance we avoid making changes to the painter's pen. // So we first take a copy of the actual painter pen, make changes to it // and only if the resulting pen is different from the actual pen // we replace the painter's pen with our new pen. // We want to avoid the mandatory detach in QPen::setColor(), // so we carefully check whether applying the setter is needed const QColor linePaintedColor = (!isOutline && (lineStyle.cosmeticOutline() && lineStyle.penStyle() == Qt::SolidLine)) ? style->polyStyle().paintedColor() : lineStyle.paintedColor(); if (currentPen.color() != linePaintedColor) { if (linePaintedColor.alpha() == 255) { currentPen.setColor(linePaintedColor); } else { if ( painter->mapQuality() != Marble::HighQuality && painter->mapQuality() != Marble::PrintQuality ) { QColor penColor = linePaintedColor; if (penColor.alpha() != 0) { penColor.setAlpha( 255 ); } if (currentPen.color() != penColor) { currentPen.setColor( penColor ); } } else { currentPen.setColor(linePaintedColor); } } } const float lineWidth = lineStyle.width(); const float linePhysicalWidth = lineStyle.physicalWidth(); float newLineWidth = lineWidth; if (linePhysicalWidth != 0.0) { const float scaledLinePhysicalWidth = float(viewport->radius()) / EARTH_RADIUS * linePhysicalWidth; newLineWidth = scaledLinePhysicalWidth > lineWidth ? scaledLinePhysicalWidth : lineWidth; } if (!isOutline && lineStyle.cosmeticOutline() && lineStyle.penStyle() == Qt::SolidLine) { if (newLineWidth > 2.5) { newLineWidth -= 2.0; } } const qreal lineDrawThreshold = isOutline ? 2.5 : 0.5; // We want to avoid the mandatory detach in QPen::setWidthF(), // so we carefully check whether applying the setter is needed if (currentPen.widthF() != newLineWidth && newLineWidth != 0.0) { if (newLineWidth < lineDrawThreshold) { if (painter->pen().style() != Qt::NoPen) { painter->setPen(Qt::NoPen); } return false; // Don't draw any outline and abort painter configuration early } currentPen.setWidthF(newLineWidth); } // No need to avoid detaches inside QPen::setCapsStyle() since Qt does that for us const Qt::PenCapStyle lineCapStyle = lineStyle.capStyle(); currentPen.setCapStyle(lineCapStyle); // No need to avoid detaches inside QPen::setStyle() since Qt does that for us if (painter->mapQuality() == HighQuality || painter->mapQuality() == PrintQuality) { const Qt::PenStyle linePenStyle = lineStyle.penStyle(); currentPen.setStyle(linePenStyle); if (linePenStyle == Qt::CustomDashLine) { // We want to avoid the mandatory detach in QPen::setDashPattern(), // so we carefully check whether applying the setter is needed if (currentPen.dashPattern() != lineStyle.dashPattern()) { currentPen.setDashPattern(lineStyle.dashPattern()); } } } else { currentPen.setStyle(Qt::SolidLine); } if ( painter->pen() != currentPen ) { painter->setPen( currentPen ); } // Set the background if (!isOutline) { if (lineStyle.background()) { QBrush brush = painter->background(); brush.setColor(style->polyStyle().paintedColor()); painter->setBackground( brush ); painter->setBackgroundMode( Qt::OpaqueMode ); } else { painter->setBackground(QBrush(Qt::transparent)); painter->setBackgroundMode(Qt::TransparentMode); } } else { painter->setBackground(QBrush(Qt::transparent)); painter->setBackgroundMode(Qt::TransparentMode); } } return true; } bool GeoLineStringGraphicsItem::configurePainterForLabel(GeoPainter *painter, const ViewportParams *viewport, LabelPositionFlags &labelPositionFlags) const { QPen currentPen = painter->pen(); GeoDataStyle::ConstPtr style = this->style(); if (!style) { painter->setPen( QPen() ); } else { const GeoDataLineStyle& lineStyle = style->lineStyle(); // To save performance we avoid making changes to the painter's pen. // So we first take a copy of the actual painter pen, make changes to it // and only if the resulting pen is different from the actual pen // we replace the painter's pen with our new pen. // We want to avoid the mandatory detach in QPen::setColor(), // so we carefully check whether applying the setter is needed const float lineWidth = lineStyle.width(); const float linePhysicalWidth = lineStyle.physicalWidth(); float newLineWidth = lineWidth; if (linePhysicalWidth != 0.0) { const float scaledLinePhysicalWidth = float(viewport->radius()) / EARTH_RADIUS * linePhysicalWidth; newLineWidth = scaledLinePhysicalWidth > lineWidth ? scaledLinePhysicalWidth : lineWidth; } // We want to avoid the mandatory detach in QPen::setWidthF(), // so we carefully check whether applying the setter is needed if (currentPen.widthF() != newLineWidth && newLineWidth != 0.0) { if (newLineWidth < 6.0) { return false; // Don't draw any labels and abort painter configuration early } currentPen.setWidthF(newLineWidth); } if ( painter->pen() != currentPen ) { painter->setPen( currentPen ); } // else qDebug() << "Detach and painter change successfully Avoided!" << Q_FUNC_INFO; if (painter->brush().color() != Qt::transparent) { painter->setBrush(QColor(Qt::transparent)); } if (painter->backgroundMode() == Qt::OpaqueMode) { painter->setBackgroundMode(Qt::TransparentMode); painter->setBackground(QBrush(Qt::transparent)); } // label styles const GeoDataLabelStyle& labelStyle = style->labelStyle(); painter->setFont(labelStyle.font() ); switch (labelStyle.alignment()) { case GeoDataLabelStyle::Corner: case GeoDataLabelStyle::Right: labelPositionFlags |= LineStart; break; case GeoDataLabelStyle::Center: labelPositionFlags |= LineCenter; break; } } return true; } bool GeoLineStringGraphicsItem::canMerge(const GeoDataCoordinates &a, const GeoDataCoordinates &b) { return distanceSphere(a, b) * EARTH_RADIUS < 0.1; } } diff --git a/src/lib/marble/routing/RoutingInputWidget.cpp b/src/lib/marble/routing/RoutingInputWidget.cpp index e07d44de7..4b6e32b22 100644 --- a/src/lib/marble/routing/RoutingInputWidget.cpp +++ b/src/lib/marble/routing/RoutingInputWidget.cpp @@ -1,504 +1,504 @@ // // This file is part of the Marble Virtual Globe. // // This program is free software licensed under the GNU LGPL. You can // find a copy of this license in LICENSE.txt in the top directory of // the source code. // // Copyright 2010 Dennis Nienhüser // Copyright 2012 Illya Kovalevskyy // #include "RoutingInputWidget.h" #include "MarblePlacemarkModel.h" #include "RouteRequest.h" #ifdef MARBLE_NO_WEBKITWIDGETS #include "NullTinyWebBrowser.h" #else #include "TinyWebBrowser.h" #endif #include "BookmarkManager.h" #include "MarbleModel.h" #include "MarbleWidget.h" #include "routing/RoutingManager.h" #include "GeoDataPlacemark.h" #include "GeoDataFolder.h" #include "GeoDataExtendedData.h" #include "GeoDataData.h" #include "PositionTracking.h" #include "ReverseGeocodingRunnerManager.h" #include "SearchRunnerManager.h" #include "MarbleLineEdit.h" #include "GoToDialog.h" #include #include #include #include #include #include #include namespace Marble { /** * A MarbleLineEdit that swallows enter/return pressed * key events */ class RoutingInputLineEdit : public MarbleLineEdit { public: explicit RoutingInputLineEdit( QWidget *parent = 0 ); protected: void keyPressEvent(QKeyEvent *) override; }; class RoutingInputWidgetPrivate { public: MarbleModel* m_marbleModel; RoutingInputLineEdit *m_lineEdit; QPushButton* m_removeButton; SearchRunnerManager m_placemarkRunnerManager; ReverseGeocodingRunnerManager m_reverseGeocodingRunnerManager; MarblePlacemarkModel *m_placemarkModel; RouteRequest *m_route; int m_index; QTimer m_nominatimTimer; QAction* m_bookmarkAction; QAction* m_mapInput; QAction* m_currentLocationAction; QAction* m_centerAction; QMenu *m_menu; /** Constructor */ RoutingInputWidgetPrivate( MarbleModel* model, int index, QWidget *parent ); /** Initiate reverse geocoding request to download address */ void adjustText(); void createMenu( RoutingInputWidget *parent ); QMenu* createBookmarkMenu( RoutingInputWidget *parent ); static void createBookmarkActions( QMenu* menu, GeoDataFolder* bookmarksFolder, QObject *parent ); static QPixmap addDropDownIndicator( const QPixmap &pixmap ); void updateDescription(); }; void RoutingInputWidgetPrivate::updateDescription() { GeoDataPlacemark const placemark = (*m_route)[m_index]; GeoDataExtendedData const address = placemark.extendedData(); if (address.contains(QStringLiteral("road")) && address.contains(QStringLiteral("city"))) { QString const road = address.value(QStringLiteral("road")).value().toString(); QString const city = address.value(QStringLiteral("city")).value().toString(); if (address.contains(QStringLiteral("house_number"))) { QString const houseNumber = address.value(QStringLiteral("house_number")).value().toString(); QString const name = QObject::tr("%1 %2, %3", "An address with parameters %1=house number, %2=road, %3=city"); - m_lineEdit->setText( name.arg( houseNumber ).arg( road ).arg( city ) ); + m_lineEdit->setText( name.arg( houseNumber, road, city ) ); } else { QString const name = QObject::tr("%2, %3", "An address with parameters %1=road, %2=city"); - m_lineEdit->setText( name.arg( road ).arg( city ) ); + m_lineEdit->setText( name.arg( road, city ) ); } } else if ( m_route->name( m_index ).isEmpty() ) { if ( !placemark.address().isEmpty() ) { m_lineEdit->setText( placemark.address() ); } else { m_lineEdit->setText( placemark.coordinate().toString().trimmed() ); } } else { m_lineEdit->setText( placemark.name() ); } m_lineEdit->setCursorPosition( 0 ); } RoutingInputLineEdit::RoutingInputLineEdit( QWidget *parent ) : MarbleLineEdit( parent ) { setPlaceholderText( QObject::tr( "Address or search term..." ) ); } void RoutingInputLineEdit::keyPressEvent(QKeyEvent *event) { MarbleLineEdit::keyPressEvent( event ); bool const returnPressed = event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter; if ( returnPressed ) { event->accept(); } } RoutingInputWidgetPrivate::RoutingInputWidgetPrivate( MarbleModel* model, int index, QWidget *parent ) : m_marbleModel( model ), m_lineEdit( 0 ), m_placemarkRunnerManager( m_marbleModel ), m_reverseGeocodingRunnerManager( m_marbleModel ), m_placemarkModel( 0 ), m_route( m_marbleModel->routingManager()->routeRequest() ), m_index( index ), m_bookmarkAction( 0 ), m_mapInput( 0 ), m_currentLocationAction( 0 ), m_centerAction( 0 ), m_menu( 0 ) { m_lineEdit = new RoutingInputLineEdit( parent ); m_lineEdit->setDecorator( addDropDownIndicator( m_route->pixmap( m_index ) ) ); m_removeButton = new QPushButton( parent ); m_removeButton->setIcon(QIcon(QStringLiteral(":/marble/routing/icon-remove.png"))); m_removeButton->setToolTip( QObject::tr( "Remove via point" ) ); m_removeButton->setFlat( true ); m_removeButton->setMaximumWidth( 18 ); m_nominatimTimer.setInterval( 1000 ); m_nominatimTimer.setSingleShot( true ); } void RoutingInputWidgetPrivate::adjustText() { m_nominatimTimer.start(); } void RoutingInputWidgetPrivate::createMenu( RoutingInputWidget *parent ) { QMenu* result = new QMenu( parent ); m_centerAction = result->addAction( QIcon( m_route->pixmap( m_index ) ), QObject::tr( "&Center Map here" ), parent, SLOT(requestActivity()) ); result->addSeparator(); m_currentLocationAction = result->addAction( QIcon(QStringLiteral(":/icons/gps.png")), QObject::tr("Current &Location"), parent, SLOT(setCurrentLocation()) ); m_currentLocationAction->setEnabled( false ); m_mapInput = result->addAction(QIcon(QStringLiteral(":/icons/crosshairs.png")), QObject::tr("From &Map...")); m_mapInput->setCheckable( true ); QObject::connect( m_mapInput, SIGNAL(triggered(bool)), parent, SLOT(setMapInputModeEnabled(bool)) ); m_bookmarkAction = result->addAction(QIcon(QStringLiteral(":/icons/bookmarks.png")), QObject::tr("From &Bookmark")); m_bookmarkAction->setMenu( createBookmarkMenu( parent ) ); m_menu = result; } QMenu* RoutingInputWidgetPrivate::createBookmarkMenu( RoutingInputWidget *parent ) { QMenu* result = new QMenu( parent ); result->addAction(QIcon(QStringLiteral(":/icons/go-home.png")), QObject::tr("&Home"), parent, SLOT(setHomePosition())); QVector folders = m_marbleModel->bookmarkManager()->folders(); if ( folders.size() == 1 ) { createBookmarkActions( result, folders.first(), parent ); } else { QVector::const_iterator i = folders.constBegin(); QVector::const_iterator end = folders.constEnd(); for (; i != end; ++i ) { QMenu* menu = result->addMenu(QIcon(QStringLiteral(":/icons/folder-bookmark.png")), (*i)->name()); createBookmarkActions( menu, *i, parent ); } } return result; } void RoutingInputWidgetPrivate::createBookmarkActions( QMenu* menu, GeoDataFolder* bookmarksFolder, QObject *parent ) { QVector bookmarks = bookmarksFolder->placemarkList(); QVector::const_iterator i = bookmarks.constBegin(); QVector::const_iterator end = bookmarks.constEnd(); for (; i != end; ++i ) { QAction *bookmarkAction = new QAction( (*i)->name(), parent ); bookmarkAction->setData( qVariantFromValue( (*i)->coordinate() ) ); menu->addAction( bookmarkAction ); QObject::connect( menu, SIGNAL(triggered(QAction*)), parent, SLOT(setBookmarkPosition(QAction*)) ); } } QPixmap RoutingInputWidgetPrivate::addDropDownIndicator(const QPixmap &pixmap) { QPixmap result( pixmap.size() + QSize( 8, pixmap.height() ) ); result.fill( QColor( Qt::transparent ) ); QPainter painter( &result ); painter.drawPixmap( 0, 0, pixmap ); QPoint const one( pixmap.width() + 1, pixmap.height() - 8 ); QPoint const two( one.x() + 6, one.y() ); QPoint const three( one.x() + 3, one.y() + 4 ); painter.setRenderHint( QPainter::Antialiasing, true ); painter.setPen( Qt::NoPen ); painter.setBrush( QColor( Oxygen::aluminumGray4 ) ); painter.drawConvexPolygon( QPolygon() << one << two << three ); return result; } RoutingInputWidget::RoutingInputWidget( MarbleModel* model, int index, QWidget *parent ) : QWidget( parent ), d( new RoutingInputWidgetPrivate( model, index, this ) ) { QHBoxLayout *layout = new QHBoxLayout( this ); layout->setSizeConstraint( QLayout::SetMinimumSize ); layout->setSpacing( 0 ); layout->setMargin( 0 ); layout->addWidget( d->m_lineEdit ); layout->addWidget( d->m_removeButton ); bool const smallScreen = MarbleGlobal::getInstance()->profiles() & MarbleGlobal::SmallScreen; if ( smallScreen ) { connect( d->m_lineEdit, SIGNAL(decoratorButtonClicked()), this, SLOT(openTargetSelectionDialog()) ); } else { d->createMenu( this ); connect(d->m_lineEdit, SIGNAL(decoratorButtonClicked()), this, SLOT(showMenu())); } connect( d->m_removeButton, SIGNAL(clicked()), this, SLOT(requestRemoval()) ); connect( d->m_marbleModel->bookmarkManager(), SIGNAL(bookmarksChanged()), this, SLOT(reloadBookmarks()) ); connect( d->m_marbleModel->positionTracking(), SIGNAL(statusChanged(PositionProviderStatus)), this, SLOT(updateCurrentLocationButton(PositionProviderStatus)) ); connect( &d->m_placemarkRunnerManager, SIGNAL(searchResultChanged(QAbstractItemModel*)), this, SLOT(setPlacemarkModel(QAbstractItemModel*)) ); connect( &d->m_reverseGeocodingRunnerManager, SIGNAL(reverseGeocodingFinished(GeoDataCoordinates,GeoDataPlacemark)), this, SLOT(retrieveReverseGeocodingResult(GeoDataCoordinates,GeoDataPlacemark)) ); connect( d->m_lineEdit, SIGNAL(returnPressed()), this, SLOT(findPlacemarks()) ); connect( d->m_lineEdit, SIGNAL(textEdited(QString)), this, SLOT(setInvalid()) ); connect( &d->m_placemarkRunnerManager, SIGNAL(searchFinished(QString)), this, SLOT(finishSearch()) ); connect( d->m_marbleModel->routingManager()->routeRequest(), SIGNAL(positionChanged(int,GeoDataCoordinates)), this, SLOT(updatePosition(int,GeoDataCoordinates)) ); connect( &d->m_nominatimTimer, SIGNAL(timeout()), this, SLOT(reverseGeocoding()) ); connect( this, SIGNAL(targetValidityChanged(bool)), this, SLOT(updateCenterButton(bool)) ); updateCenterButton( hasTargetPosition() ); d->adjustText(); } RoutingInputWidget::~RoutingInputWidget() { delete d; } void RoutingInputWidget::reverseGeocoding() { if ( !hasTargetPosition() ) { return; } QString const name = d->m_route->name( d->m_index ); if ( name.isEmpty() || name == tr( "Current Location" ) ) { d->m_reverseGeocodingRunnerManager.reverseGeocoding( targetPosition() ); } else { d->updateDescription(); } } void RoutingInputWidget::setPlacemarkModel( QAbstractItemModel *model ) { d->m_placemarkModel = dynamic_cast(model); } void RoutingInputWidget::setTargetPosition( const GeoDataCoordinates &position, const QString &name ) { if ( d->m_mapInput ) { d->m_mapInput->setChecked( false ); } d->m_route->setPosition( d->m_index, position, name ); if ( !name.isEmpty() ) { d->updateDescription(); } emit targetValidityChanged( true ); } bool RoutingInputWidget::hasTargetPosition() const { return targetPosition().isValid(); } GeoDataCoordinates RoutingInputWidget::targetPosition() const { if ( d->m_index < d->m_route->size() ) { return d->m_route->at( d->m_index ); } else { return GeoDataCoordinates(); } } void RoutingInputWidget::findPlacemarks() { QString text = d->m_lineEdit->text(); if ( text.isEmpty() ) { setInvalid(); } else { d->m_lineEdit->setBusy(true); d->m_placemarkRunnerManager.findPlacemarks( text ); } } MarblePlacemarkModel *RoutingInputWidget::searchResultModel() { return d->m_placemarkModel; } void RoutingInputWidget::requestActivity() { if ( hasTargetPosition() ) { emit activityRequest( this ); } } void RoutingInputWidget::requestRemoval() { emit removalRequest( this ); } bool RoutingInputWidget::hasInput() const { return !d->m_lineEdit->text().isEmpty(); } void RoutingInputWidget::setMapInputModeEnabled( bool enabled ) { emit mapInputModeEnabled( this, enabled ); } void RoutingInputWidget::finishSearch() { d->m_lineEdit->setBusy(false); emit searchFinished( this ); } void RoutingInputWidget::setInvalid() { d->m_route->setPosition( d->m_index, GeoDataCoordinates() ); emit targetValidityChanged( false ); } void RoutingInputWidget::abortMapInputRequest() { if ( d->m_mapInput ) { d->m_mapInput->setChecked( false ); } } void RoutingInputWidget::setIndex( int index ) { d->m_index = index; d->m_lineEdit->setBusy(false); d->m_lineEdit->setDecorator( d->addDropDownIndicator( d->m_route->pixmap( index ) ) ); } void RoutingInputWidget::updatePosition( int index, const GeoDataCoordinates & ) { if ( index == d->m_index ) { d->m_lineEdit->setBusy(false); emit targetValidityChanged( hasTargetPosition() ); d->adjustText(); } } void RoutingInputWidget::clear() { d->m_nominatimTimer.stop(); d->m_lineEdit->setBusy(false); d->m_route->setPosition( d->m_index, GeoDataCoordinates() ); d->m_lineEdit->clear(); emit targetValidityChanged( false ); } void RoutingInputWidget::retrieveReverseGeocodingResult( const GeoDataCoordinates &, const GeoDataPlacemark &placemark ) { (*d->m_route)[d->m_index] = placemark; d->updateDescription(); } void RoutingInputWidget::reloadBookmarks() { if ( d->m_bookmarkAction ) { d->m_bookmarkAction->setMenu( d->createBookmarkMenu( this ) ); } } void RoutingInputWidget::setHomePosition() { qreal lon( 0.0 ), lat( 0.0 ); int zoom( 0 ); d->m_marbleModel->home( lon, lat, zoom ); GeoDataCoordinates home( lon, lat, 0.0, GeoDataCoordinates::Degree ); setTargetPosition( home ); requestActivity(); } void RoutingInputWidget::updateCurrentLocationButton( PositionProviderStatus status ) { if ( d->m_currentLocationAction ) { d->m_currentLocationAction->setEnabled( status == PositionProviderStatusAvailable ); } } void RoutingInputWidget::setCurrentLocation() { setTargetPosition( d->m_marbleModel->positionTracking()->currentLocation() ); requestActivity(); } void RoutingInputWidget::updateCenterButton( bool hasPosition ) { if ( d->m_centerAction ) { d->m_centerAction->setEnabled( hasPosition ); } } void RoutingInputWidget::setBookmarkPosition( QAction* bookmark ) { if ( !bookmark->data().isNull() ) { setTargetPosition( bookmark->data().value() ); requestActivity(); } } void RoutingInputWidget::openTargetSelectionDialog() { QPointer dialog = new GoToDialog( d->m_marbleModel, this ); dialog->setWindowTitle( tr( "Choose Placemark" ) ); dialog->setShowRoutingItems( false ); dialog->setSearchEnabled( false ); if ( dialog->exec() == QDialog::Accepted ) { const GeoDataCoordinates coordinates = dialog->coordinates(); setTargetPosition( coordinates ); } delete dialog; } void RoutingInputWidget::showMenu() { d->m_menu->exec( mapToGlobal( QPoint( 0, size().height() ) ) ); } } // namespace Marble #include "moc_RoutingInputWidget.cpp" diff --git a/src/lib/marble/routing/SpeakersModel.cpp b/src/lib/marble/routing/SpeakersModel.cpp index 38be9fa69..621477a00 100644 --- a/src/lib/marble/routing/SpeakersModel.cpp +++ b/src/lib/marble/routing/SpeakersModel.cpp @@ -1,228 +1,228 @@ // // This file is part of the Marble Virtual Globe. // // This program is free software licensed under the GNU LGPL. You can // find a copy of this license in LICENSE.txt in the top directory of // the source code. // // Copyright 2012 Dennis Nienhüser // #include "SpeakersModel.h" #include "MarbleDirs.h" #include "NewstuffModel.h" namespace Marble { class SpeakersModelItem { public: QFileInfo m_file; int m_newstuffIndex; SpeakersModelItem(); static bool lessThan( const SpeakersModelItem& one, const SpeakersModelItem& two ); }; class SpeakersModelPrivate { public: SpeakersModel* m_parent; QList m_speakers; NewstuffModel m_newstuffModel; explicit SpeakersModelPrivate( SpeakersModel* parent ); void fillModel(); void handleInstallationProgress( int row, qreal progress ); void handleInstallation( int ); QHash m_roleNames; }; SpeakersModelItem::SpeakersModelItem() : m_newstuffIndex( -1 ) { // nothing to do } bool SpeakersModelItem::lessThan( const SpeakersModelItem& one, const SpeakersModelItem& two ) { return one.m_file.fileName() < two.m_file.fileName(); } SpeakersModelPrivate::SpeakersModelPrivate( SpeakersModel* parent ) : m_parent( parent ) { m_newstuffModel.setTargetDirectory(MarbleDirs::localPath() + QLatin1String("/audio/speakers")); m_newstuffModel.setProvider( "http://edu.kde.org/marble/newstuff/speakers.xml" ); QObject::connect( &m_newstuffModel, SIGNAL(modelReset()), m_parent, SLOT(fillModel()) ); QObject::connect( &m_newstuffModel, SIGNAL(installationProgressed(int,qreal)), m_parent, SLOT(handleInstallationProgress(int,qreal)) ); QObject::connect( &m_newstuffModel, SIGNAL(installationFinished(int)), m_parent, SLOT(handleInstallation(int)) ); } void SpeakersModelPrivate::fillModel() { m_speakers.clear(); QStringList const baseDirs = QStringList() << MarbleDirs::systemPath() << MarbleDirs::localPath(); for ( const QString &baseDir: baseDirs ) { const QString base = baseDir + QLatin1String("/audio/speakers/"); QDir::Filters filter = QDir::Readable | QDir::Dirs | QDir::NoDotAndDotDot; QFileInfoList subdirs = QDir( base ).entryInfoList( filter, QDir::Name ); for( const QFileInfo &file: subdirs ) { SpeakersModelItem item; item.m_file = file; m_speakers << item; } } for ( int i=0; ibeginResetModel(); m_parent->endResetModel(); emit m_parent->countChanged(); } void SpeakersModelPrivate::handleInstallation( int row ) { for ( int j=0; jindex( j ); emit m_parent->dataChanged( affected, affected ); emit m_parent->installationFinished( j ); } } } void SpeakersModelPrivate::handleInstallationProgress( int row, qreal progress ) { for ( int j=0; jinstallationProgressed( j, progress ); } } } SpeakersModel::SpeakersModel( QObject *parent ) : QAbstractListModel( parent ), d( new SpeakersModelPrivate( this ) ) { QHash roles; roles[Path] = "path"; roles[Name] = "name"; roles[IsLocal] = "isLocal"; roles[IsRemote] = "isRemote"; d->m_roleNames = roles; d->fillModel(); } SpeakersModel::~SpeakersModel() { delete d; } int SpeakersModel::rowCount ( const QModelIndex &parent ) const { if ( !parent.isValid() ) { return d->m_speakers.size(); } return 0; } QVariant SpeakersModel::data ( const QModelIndex &index, int role ) const { if ( index.isValid() && index.row() >= 0 && index.row() < d->m_speakers.size() ) { switch ( role ) { case Qt::DisplayRole: return d->m_speakers.at( index.row() ).m_file.fileName(); case Path: return d->m_speakers.at( index.row() ).m_file.absoluteFilePath(); case Name: return d->m_speakers.at( index.row() ).m_file.fileName(); case IsLocal: return d->m_speakers.at( index.row() ).m_file.exists(); case IsRemote: return d->m_speakers.at( index.row() ).m_newstuffIndex >= 0; } } return QVariant(); } QHash SpeakersModel::roleNames() const { return d->m_roleNames; } int SpeakersModel::indexOf( const QString &name ) { for( int i=0; im_speakers.size(); ++i ) { if ( d->m_speakers[i].m_file.absoluteFilePath() == name || d->m_speakers[i].m_file.fileName() == name ) { return i; } } return -1; } QString SpeakersModel::path( int index ) { if ( index >= 0 && index <= d->m_speakers.size() ) { return d->m_speakers[index].m_file.absoluteFilePath(); } return QString(); } void SpeakersModel::install( int index ) { if ( index >= 0 && index < d->m_speakers.size() ) { if ( d->m_speakers[index].m_newstuffIndex >= 0 ) { d->m_newstuffModel.install( d->m_speakers[index].m_newstuffIndex ); } } } bool SpeakersModel::isLocal( int idx ) const { return data( index( idx ), IsLocal ).toBool(); } bool SpeakersModel::isRemote( int idx ) const { return data( index( idx ), IsRemote ).toBool(); } int SpeakersModel::count() const { return rowCount(); } } #include "moc_SpeakersModel.cpp" diff --git a/src/lib/marble/routing/VoiceNavigationModel.cpp b/src/lib/marble/routing/VoiceNavigationModel.cpp index fee8440cd..b1574751b 100644 --- a/src/lib/marble/routing/VoiceNavigationModel.cpp +++ b/src/lib/marble/routing/VoiceNavigationModel.cpp @@ -1,412 +1,412 @@ // // This file is part of the Marble Virtual Globe. // // This program is free software licensed under the GNU LGPL. You can // find a copy of this license in LICENSE.txt in the top directory of // the source code. // // Copyright 2012 Dennis Nienhüser // #include "VoiceNavigationModel.h" #include "Route.h" #include "MarbleDirs.h" #include "MarbleDebug.h" namespace Marble { class VoiceNavigationModelPrivate { public: struct Announcement { bool announcementDone; bool turnInstructionDone; Announcement(){ announcementDone = false; turnInstructionDone = false; } }; VoiceNavigationModel* m_parent; QString m_speaker; bool m_speakerEnabled; QMap m_turnTypeMap; QMap m_announceMap; qreal m_lastDistance; qreal m_lastDistanceTraversed; GeoDataLineString m_lastRoutePath; Maneuver::Direction m_lastTurnType; GeoDataCoordinates m_lastTurnPoint; QStringList m_queue; QString m_announcementText; bool m_destinationReached; bool m_deviated; QVector m_announcementList; explicit VoiceNavigationModelPrivate( VoiceNavigationModel* parent ); void reset(); QString audioFile(const QString &name) const; QString distanceAudioFile( qreal dest ) const; QString turnTypeAudioFile( Maneuver::Direction turnType, qreal distance ); QString announcementText( Maneuver::Direction turnType, qreal distance ); void updateInstruction(const RouteSegment &segment, qreal distance, Maneuver::Direction turnType ); void updateInstruction( const QString &name ); void initializeMaps(); }; VoiceNavigationModelPrivate::VoiceNavigationModelPrivate( VoiceNavigationModel* parent ) : m_parent( parent ), m_speakerEnabled( true ), m_lastDistance( 0.0 ), m_lastDistanceTraversed( 0.0 ), m_lastTurnType( Maneuver::Unknown ), m_destinationReached( false ), m_deviated( false ) { initializeMaps(); } void VoiceNavigationModelPrivate::reset() { m_lastDistance = 0.0; m_lastDistanceTraversed = 0.0; } QString VoiceNavigationModelPrivate::audioFile( const QString &name ) const { #ifdef Q_OS_ANDROID return name; #else QStringList const formats = QStringList() << "ogg" << "mp3" << "wav"; if ( m_speakerEnabled ) { QString const audioTemplate = "%1/%2.%3"; for( const QString &format: formats ) { - QString const result = audioTemplate.arg( m_speaker ).arg( name ).arg( format ); + QString const result = audioTemplate.arg( m_speaker, name, format ); QFileInfo audioFile( result ); if ( audioFile.exists() ) { return result; } } } QString const audioTemplate = "audio/%1.%2"; for( const QString &format: formats ) { - QString const result = MarbleDirs::path( audioTemplate.arg( name ).arg( format ) ); + QString const result = MarbleDirs::path( audioTemplate.arg( name, format ) ); if ( !result.isEmpty() ) { return result; } } return QString(); #endif } QString VoiceNavigationModelPrivate::distanceAudioFile( qreal dest ) const { if ( dest > 0.0 && dest < 900.0 ) { qreal minDistance = 0.0; int targetDistance = 0; QVector distances; distances << 50 << 80 << 100 << 200 << 300 << 400 << 500 << 600 << 700 << 800; for( int distance: distances ) { QString file = audioFile( QString::number( distance ) ); qreal currentDistance = qAbs( distance - dest ); if ( !file.isEmpty() && ( minDistance == 0.0 || currentDistance < minDistance ) ) { minDistance = currentDistance; targetDistance = distance; } } if ( targetDistance > 0 ) { return audioFile( QString::number( targetDistance ) ); } } return QString(); } QString VoiceNavigationModelPrivate::turnTypeAudioFile( Maneuver::Direction turnType, qreal distance ) { bool const announce = distance >= 75; QMap const & map = announce ? m_announceMap : m_turnTypeMap; if ( m_speakerEnabled && map.contains( turnType ) ) { return audioFile( map[turnType] ); } return audioFile( announce ? "ListEnd" : "AppPositive" ); } QString VoiceNavigationModelPrivate::announcementText( Maneuver::Direction turnType, qreal distance ) { QString announcementText = QString(""); if (distance >= 75) { announcementText = QString("In "+distanceAudioFile(distance)+" meters, "); } switch (turnType) { case Maneuver::Continue: case Maneuver::Straight: announcementText += QString("Continue straight"); break; case Maneuver::SlightRight: announcementText += QString("Turn slight right"); break; case Maneuver::SlightLeft: announcementText += QString("Turn slight left"); break; case Maneuver::Right: case Maneuver::SharpRight: announcementText += QString("Turn right"); break; case Maneuver::Left: case Maneuver::SharpLeft: announcementText += QString("Turn left"); break; case Maneuver::TurnAround: announcementText += QString("Take a U-turn"); break; case Maneuver::ExitLeft: announcementText += QString("Exit left"); break; case Maneuver::ExitRight: announcementText += QString("Exit right"); break; case Maneuver::RoundaboutFirstExit: announcementText += QString("Take the first exit"); break; case Maneuver::RoundaboutSecondExit: announcementText += QString("Take the second exit"); break; case Maneuver::RoundaboutThirdExit: announcementText += QString("Take the third exit"); break; default: announcementText = QString(""); break; } return announcementText; } void VoiceNavigationModelPrivate::updateInstruction( const RouteSegment & segment, qreal distance, Maneuver::Direction turnType ) { QString turnTypeAudio = turnTypeAudioFile( turnType, distance ); if ( turnTypeAudio.isEmpty() ) { mDebug() << "Missing audio file for turn type " << turnType << " and speaker " << m_speaker; return; } m_queue.clear(); m_queue << turnTypeAudio; m_announcementText = announcementText(turnType, distance); qreal nextSegmentDistance = segment.nextRouteSegment().distance(); Maneuver::Direction nextSegmentDirection = segment.nextRouteSegment().nextRouteSegment().maneuver().direction(); if (!m_announcementText.isEmpty() && distance < 75 && nextSegmentDistance != 0 && nextSegmentDistance < 75) { QString nextSegmentAnnouncementText = announcementText(nextSegmentDirection, nextSegmentDistance); if (!nextSegmentAnnouncementText.isEmpty()) { m_announcementText += QLatin1String(", then ") + nextSegmentAnnouncementText; } } emit m_parent->instructionChanged(); } void VoiceNavigationModelPrivate::updateInstruction( const QString &name ) { m_queue.clear(); m_queue << audioFile( name ); m_announcementText = name; emit m_parent->instructionChanged(); } void VoiceNavigationModelPrivate::initializeMaps() { m_turnTypeMap.clear(); m_announceMap.clear(); m_announceMap[Maneuver::Continue] = "Straight"; // none of our voice navigation commands fits, so leave out // Maneuver::Merge here to have a sound play instead m_announceMap[Maneuver::Straight] = "Straight"; m_announceMap[Maneuver::SlightRight] = "AhKeepRight"; m_announceMap[Maneuver::Right] = "AhRightTurn"; m_announceMap[Maneuver::SharpRight] = "AhRightTurn"; m_announceMap[Maneuver::TurnAround] = "AhUTurn"; m_announceMap[Maneuver::SharpLeft] = "AhLeftTurn"; m_announceMap[Maneuver::Left] = "AhLeftTurn"; m_announceMap[Maneuver::SlightLeft] = "AhKeepLeft"; m_announceMap[Maneuver::RoundaboutFirstExit] = "RbExit1"; m_announceMap[Maneuver::RoundaboutSecondExit] = "RbExit2"; m_announceMap[Maneuver::RoundaboutThirdExit] = "RbExit3"; m_announceMap[Maneuver::ExitLeft] = "AhExitLeft"; m_announceMap[Maneuver::ExitRight] = "AhExitRight"; m_turnTypeMap[Maneuver::Continue] = "Straight"; // none of our voice navigation commands fits, so leave out // Maneuver::Merge here to have a sound play instead m_turnTypeMap[Maneuver::Straight] = "Straight"; m_turnTypeMap[Maneuver::SlightRight] = "BearRight"; m_turnTypeMap[Maneuver::Right] = "TurnRight"; m_turnTypeMap[Maneuver::SharpRight] = "SharpRight"; m_turnTypeMap[Maneuver::TurnAround] = "UTurn"; m_turnTypeMap[Maneuver::SharpLeft] = "SharpLeft"; m_turnTypeMap[Maneuver::Left] = "TurnLeft"; m_turnTypeMap[Maneuver::SlightLeft] = "BearLeft"; m_turnTypeMap[Maneuver::RoundaboutFirstExit] = ""; m_turnTypeMap[Maneuver::RoundaboutSecondExit] = ""; m_turnTypeMap[Maneuver::RoundaboutThirdExit] = ""; m_turnTypeMap[Maneuver::ExitLeft] = "TurnLeft"; m_turnTypeMap[Maneuver::ExitRight] = "TurnRight"; } VoiceNavigationModel::VoiceNavigationModel( QObject *parent ) : QObject( parent ), d( new VoiceNavigationModelPrivate( this ) ) { // nothing to do } VoiceNavigationModel::~VoiceNavigationModel() { delete d; } QString VoiceNavigationModel::speaker() const { return d->m_speaker; } void VoiceNavigationModel::setSpeaker(const QString &speaker) { if ( speaker != d->m_speaker ) { QFileInfo speakerDir = QFileInfo( speaker ); if ( !speakerDir.exists() ) { d->m_speaker = MarbleDirs::path(QLatin1String("/audio/speakers/") + speaker); } else { d->m_speaker = speaker; } emit speakerChanged(); emit previewChanged(); } } bool VoiceNavigationModel::isSpeakerEnabled() const { return d->m_speakerEnabled; } void VoiceNavigationModel::setSpeakerEnabled( bool enabled ) { if ( enabled != d->m_speakerEnabled ) { d->m_speakerEnabled = enabled; emit isSpeakerEnabledChanged(); emit previewChanged(); } } void VoiceNavigationModel::reset() { d->reset(); } void VoiceNavigationModel::update(const Route &route, qreal distanceManuever, qreal distanceTarget, bool deviated ) { if (d->m_lastRoutePath != route.path()){ d->m_announcementList.clear(); d->m_announcementList.resize(route.size()); d->m_lastRoutePath = route.path(); } if ( d->m_destinationReached && distanceTarget < 250 ) { return; } if ( !d->m_destinationReached && distanceTarget < 50 ) { d->m_destinationReached = true; d->updateInstruction( d->m_speakerEnabled ? "You have arrived at your destination" : "AppPositive" ); return; } if ( distanceTarget > 150 ) { d->m_destinationReached = false; } if ( deviated && !d->m_deviated ) { d->updateInstruction( d->m_speakerEnabled ? "Deviated from the route" : "ListEnd" ); } d->m_deviated = deviated; if ( deviated ) { return; } Maneuver::Direction turnType = route.currentSegment().nextRouteSegment().maneuver().direction(); if ( !( d->m_lastTurnPoint == route.currentSegment().nextRouteSegment().maneuver().position() ) || turnType != d->m_lastTurnType ) { d->m_lastTurnPoint = route.currentSegment().nextRouteSegment().maneuver().position(); d->reset(); } int index = route.indexOf(route.currentSegment()); qreal const distanceTraversed = route.currentSegment().distance() - distanceManuever; bool const minDistanceTraversed = d->m_lastDistanceTraversed < 40 && distanceTraversed >= 40; bool const announcementAfterTurn = minDistanceTraversed && distanceManuever >= 75; bool const announcement = ( d->m_lastDistance > 850 || announcementAfterTurn ) && distanceManuever <= 850; bool const turn = ( d->m_lastDistance == 0 || d->m_lastDistance > 75 ) && distanceManuever <= 75; bool const announcementDone = d->m_announcementList[index].announcementDone; bool const turnInstructionDone = d->m_announcementList[index].turnInstructionDone; if ( ( announcement && !announcementDone ) || ( turn && !turnInstructionDone ) ) { d->updateInstruction( route.currentSegment(), distanceManuever, turnType ); VoiceNavigationModelPrivate::Announcement & curAnnouncement = d->m_announcementList[index]; if (announcement){ curAnnouncement.announcementDone = true; } if (turn){ curAnnouncement.turnInstructionDone = true; } } d->m_lastTurnType = turnType; d->m_lastDistance = distanceManuever; d->m_lastDistanceTraversed = distanceTraversed; } QString VoiceNavigationModel::preview() const { return d->audioFile( d->m_speakerEnabled ? "The Marble team wishes you a pleasant and safe journey!" : "AppPositive" ); } QString VoiceNavigationModel::instruction() const { return d->m_announcementText; } } #include "moc_VoiceNavigationModel.cpp"