File indexing completed on 2024-04-21 03:49:30

0001 // SPDX-License-Identifier: LGPL-2.1-or-later
0002 //
0003 // SPDX-FileCopyrightText: 2004-2007 Torsten Rahn <tackat@kde.org>
0004 // SPDX-FileCopyrightText: 2007 Inge Wallin <ingwa@kde.org>
0005 // SPDX-FileCopyrightText: 2007 Thomas Zander <zander@kde.org>
0006 // SPDX-FileCopyrightText: 2010 Bastian Holst <bastianholst@gmx.de>
0007 //
0008 
0009 // Self
0010 #include "CurrentLocationWidget.h"
0011 
0012 // Marble
0013 #include "MarbleDebug.h"
0014 #include "MarbleLocale.h"
0015 #include "MarbleModel.h"
0016 #include "MarbleWidget.h"
0017 #include "MarbleWidgetPopupMenu.h"
0018 #include "GeoDataCoordinates.h"
0019 #include "PositionProviderPlugin.h"
0020 #include "PluginManager.h"
0021 #include "PositionTracking.h"
0022 #include "routing/RoutingManager.h"
0023 
0024 using namespace Marble;
0025 /* TRANSLATOR Marble::CurrentLocationWidget */
0026 
0027 // Ui
0028 #include "ui_CurrentLocationWidget.h"
0029 
0030 #include <QDateTime>
0031 #include <QFileDialog>
0032 #include <QMessageBox>
0033 
0034 namespace Marble
0035 {
0036 
0037 class CurrentLocationWidgetPrivate
0038 {
0039  public:
0040     CurrentLocationWidgetPrivate();
0041 
0042     Ui::CurrentLocationWidget      m_currentLocationUi;
0043     MarbleWidget                  *m_widget;
0044     AutoNavigation *m_adjustNavigation;
0045 
0046     QList<const PositionProviderPlugin*> m_positionProviderPlugins;
0047     GeoDataCoordinates             m_currentPosition;
0048 
0049     QString m_lastOpenPath;
0050     QString m_lastSavePath;
0051 
0052     void receiveGpsCoordinates( const GeoDataCoordinates &position, qreal speed );
0053     void adjustPositionTrackingStatus( PositionProviderStatus status );
0054     void changePositionProvider( const QString &provider );
0055     void trackPlacemark();
0056     void centerOnCurrentLocation();
0057     void updateRecenterComboBox( AutoNavigation::CenterMode centerMode );
0058     void updateAutoZoomCheckBox( bool autoZoom );
0059     void updateActivePositionProvider( PositionProviderPlugin* );
0060     void updateGuidanceMode();
0061     void saveTrack();
0062     void openTrack();
0063     void clearTrack();
0064 };
0065 
0066 CurrentLocationWidgetPrivate::CurrentLocationWidgetPrivate()
0067     : m_widget( nullptr ),
0068       m_adjustNavigation( nullptr ),
0069       m_positionProviderPlugins(),
0070       m_currentPosition(),
0071       m_lastOpenPath(),
0072       m_lastSavePath()
0073 {
0074 }
0075 
0076 CurrentLocationWidget::CurrentLocationWidget( QWidget *parent, Qt::WindowFlags f )
0077     : QWidget( parent, f ),
0078       d( new CurrentLocationWidgetPrivate() )
0079 {
0080     d->m_currentLocationUi.setupUi( this );
0081     layout()->setMargin( 0 );
0082 
0083     connect( d->m_currentLocationUi.recenterComboBox, SIGNAL (currentIndexChanged(int)),
0084             this, SLOT(setRecenterMode(int)) );
0085 
0086     connect( d->m_currentLocationUi.autoZoomCheckBox, SIGNAL(clicked(bool)),
0087              this, SLOT(setAutoZoom(bool)) );
0088 
0089     bool const smallScreen = MarbleGlobal::getInstance()->profiles() & MarbleGlobal::SmallScreen;
0090     d->m_currentLocationUi.positionTrackingComboBox->setVisible( !smallScreen );
0091     d->m_currentLocationUi.locationLabel->setVisible( !smallScreen );
0092 }
0093 
0094 CurrentLocationWidget::~CurrentLocationWidget()
0095 {
0096     delete d;
0097 }
0098 
0099 void CurrentLocationWidget::setMarbleWidget( MarbleWidget *widget )
0100 {
0101     d->m_widget = widget;
0102 
0103     delete d->m_adjustNavigation;
0104     d->m_adjustNavigation = new AutoNavigation( widget->model(), widget->viewport(), this );
0105 
0106     const PluginManager* pluginManager = d->m_widget->model()->pluginManager();
0107     d->m_positionProviderPlugins = pluginManager->positionProviderPlugins();
0108     for( const PositionProviderPlugin *plugin: d->m_positionProviderPlugins ) {
0109         d->m_currentLocationUi.positionTrackingComboBox->addItem( plugin->guiString() );
0110     }
0111     if ( d->m_positionProviderPlugins.isEmpty() ) {
0112         d->m_currentLocationUi.positionTrackingComboBox->setEnabled( false );
0113         QString html = "<p>No Position Tracking Plugin installed.</p>";
0114         d->m_currentLocationUi.locationLabel->setText( html );
0115         d->m_currentLocationUi.locationLabel->setEnabled ( true );
0116         bool const hasTrack = !d->m_widget->model()->positionTracking()->isTrackEmpty();
0117         d->m_currentLocationUi.showTrackCheckBox->setEnabled( hasTrack );
0118         d->m_currentLocationUi.saveTrackButton->setEnabled( hasTrack );
0119         d->m_currentLocationUi.clearTrackButton->setEnabled( hasTrack );
0120     }
0121 
0122     //disconnect CurrentLocation Signals
0123     disconnect( d->m_widget->model()->positionTracking(),
0124              SIGNAL(gpsLocation(GeoDataCoordinates,qreal)),
0125              this, SLOT(receiveGpsCoordinates(GeoDataCoordinates,qreal)) );
0126     disconnect( d->m_widget->model()->positionTracking(),
0127              SIGNAL(positionProviderPluginChanged(PositionProviderPlugin*)),
0128              this, SLOT(updateActivePositionProvider(PositionProviderPlugin*)) );
0129     disconnect( d->m_currentLocationUi.positionTrackingComboBox, SIGNAL(currentIndexChanged(QString)),
0130              this, SLOT(changePositionProvider(QString)) );
0131     disconnect( d->m_currentLocationUi.locationLabel, SIGNAL(linkActivated(QString)),
0132              this, SLOT(centerOnCurrentLocation()) );
0133     disconnect( d->m_widget->model()->positionTracking(),
0134              SIGNAL(statusChanged(PositionProviderStatus)),this,
0135              SLOT(adjustPositionTrackingStatus(PositionProviderStatus)) );
0136 
0137     disconnect( d->m_widget->model(), SIGNAL(trackedPlacemarkChanged(const GeoDataPlacemark*)),
0138              this, SLOT(trackPlacemark()) );
0139 
0140     //connect CurrentLocation signals
0141     connect( d->m_widget->model()->positionTracking(),
0142              SIGNAL(gpsLocation(GeoDataCoordinates,qreal)),
0143              this, SLOT(receiveGpsCoordinates(GeoDataCoordinates,qreal)) );
0144     connect( d->m_widget->model()->positionTracking(),
0145              SIGNAL(positionProviderPluginChanged(PositionProviderPlugin*)),
0146              this, SLOT(updateActivePositionProvider(PositionProviderPlugin*)) );
0147     d->updateActivePositionProvider( d->m_widget->model()->positionTracking()->positionProviderPlugin() );
0148     connect( d->m_currentLocationUi.positionTrackingComboBox, SIGNAL(currentIndexChanged(QString)),
0149              this, SLOT(changePositionProvider(QString)) );
0150     connect( d->m_currentLocationUi.locationLabel, SIGNAL(linkActivated(QString)),
0151              this, SLOT(centerOnCurrentLocation()) );
0152     connect( d->m_widget->model()->positionTracking(),
0153              SIGNAL(statusChanged(PositionProviderStatus)), this,
0154              SLOT(adjustPositionTrackingStatus(PositionProviderStatus)) );
0155 
0156     connect( d->m_adjustNavigation, SIGNAL(recenterModeChanged(AutoNavigation::CenterMode)),
0157              this, SLOT(updateRecenterComboBox(AutoNavigation::CenterMode)) );
0158     connect( d->m_adjustNavigation, SIGNAL(autoZoomToggled(bool)),
0159              this, SLOT(updateAutoZoomCheckBox(bool)) );
0160     connect( d->m_adjustNavigation, SIGNAL(zoomIn(FlyToMode)),
0161              d->m_widget, SLOT(zoomIn(FlyToMode)) );
0162     connect( d->m_adjustNavigation, SIGNAL(zoomOut(FlyToMode)),
0163              d->m_widget, SLOT(zoomOut(FlyToMode)) );
0164     connect( d->m_adjustNavigation, SIGNAL(centerOn(GeoDataCoordinates,bool)),
0165              d->m_widget, SLOT(centerOn(GeoDataCoordinates,bool)) );
0166 
0167     connect( d->m_widget, SIGNAL(visibleLatLonAltBoxChanged(GeoDataLatLonAltBox)),
0168              d->m_adjustNavigation, SLOT(inhibitAutoAdjustments()) );
0169     connect( d->m_widget->model()->routingManager(), SIGNAL(guidanceModeEnabledChanged(bool)),
0170              this, SLOT(updateGuidanceMode()) );
0171 
0172     connect (d->m_currentLocationUi.showTrackCheckBox, SIGNAL(clicked(bool)),
0173              d->m_widget->model()->positionTracking(), SLOT(setTrackVisible(bool)));
0174     connect (d->m_currentLocationUi.showTrackCheckBox, SIGNAL(clicked(bool)),
0175              d->m_widget, SLOT(update()));
0176     if ( d->m_widget->model()->positionTracking()->trackVisible() ) {
0177         d->m_currentLocationUi.showTrackCheckBox->setCheckState(Qt::Checked);
0178     }
0179     connect ( d->m_currentLocationUi.saveTrackButton, SIGNAL(clicked(bool)),
0180               this, SLOT(saveTrack()));
0181     connect ( d->m_currentLocationUi.openTrackButton, SIGNAL(clicked(bool)),
0182               this, SLOT(openTrack()));
0183     connect (d->m_currentLocationUi.clearTrackButton, SIGNAL(clicked(bool)),
0184              this, SLOT(clearTrack()));
0185     connect( d->m_widget->model(), SIGNAL(trackedPlacemarkChanged(const GeoDataPlacemark*)),
0186              this, SLOT(trackPlacemark()) );
0187 }
0188 
0189 void CurrentLocationWidgetPrivate::adjustPositionTrackingStatus( PositionProviderStatus status )
0190 {
0191     if ( status == PositionProviderStatusAvailable ) {
0192         return;
0193     }
0194 
0195     QString html = "<html><body><p>";
0196 
0197     switch ( status ) {
0198         case PositionProviderStatusUnavailable:
0199             html += QObject::tr( "No position available." );
0200             break;
0201         case PositionProviderStatusAcquiring:
0202             html += QObject::tr( "Waiting for current location information..." );
0203             break;
0204         case PositionProviderStatusAvailable:
0205             Q_ASSERT( false );
0206             break;
0207         case PositionProviderStatusError:
0208             html += QObject::tr( "Error when determining current location: " );
0209             html += m_widget->model()->positionTracking()->error();
0210             break;
0211     }
0212 
0213     html += QLatin1String("</p></body></html>");
0214     m_currentLocationUi.locationLabel->setEnabled( true );
0215     m_currentLocationUi.locationLabel->setText( html );
0216 }
0217 
0218 void CurrentLocationWidgetPrivate::updateActivePositionProvider( PositionProviderPlugin *plugin )
0219 {
0220     m_currentLocationUi.positionTrackingComboBox->blockSignals( true );
0221     if ( !plugin ) {
0222         m_currentLocationUi.positionTrackingComboBox->setCurrentIndex( 0 );
0223     } else {
0224         for( int i=0; i<m_currentLocationUi.positionTrackingComboBox->count(); ++i ) {
0225             if ( m_currentLocationUi.positionTrackingComboBox->itemText( i ) == plugin->guiString() ) {
0226                 m_currentLocationUi.positionTrackingComboBox->setCurrentIndex( i );
0227                 break;
0228             }
0229         }
0230     }
0231     m_currentLocationUi.positionTrackingComboBox->blockSignals( false );
0232     m_currentLocationUi.recenterLabel->setEnabled( plugin );
0233     m_currentLocationUi.recenterComboBox->setEnabled( plugin );
0234     m_currentLocationUi.autoZoomCheckBox->setEnabled( plugin );
0235 
0236 }
0237 
0238 void CurrentLocationWidgetPrivate::updateGuidanceMode()
0239 {
0240     const bool enabled = m_widget->model()->routingManager()->guidanceModeEnabled();
0241 
0242     m_adjustNavigation->setAutoZoom( enabled );
0243     m_adjustNavigation->setRecenter( enabled ? AutoNavigation::RecenterOnBorder : AutoNavigation::DontRecenter );
0244 }
0245 
0246 void CurrentLocationWidgetPrivate::receiveGpsCoordinates( const GeoDataCoordinates &position, qreal speed )
0247 {
0248     m_currentPosition = position;
0249     QString unitString;
0250     QString altitudeUnitString;
0251     QString distanceUnitString;
0252     qreal unitSpeed = 0.0;
0253     qreal altitude = 0.0;
0254     qreal length = m_widget->model()->positionTracking()->length( m_widget->model()->planetRadius() );
0255 
0256     QString html = QLatin1String("<html><body>"
0257         "<table cellspacing=\"2\" cellpadding=\"2\">"
0258         "<tr><td>Longitude</td><td><a href=\"https://edu.kde.org/marble\">%1</a></td></tr>"
0259         "<tr><td>Latitude</td><td><a href=\"https://edu.kde.org/marble\">%2</a></td></tr>"
0260         "<tr><td>Altitude</td><td>%3</td></tr>"
0261         "<tr><td>Speed</td><td>%4</td></tr>"
0262         "<tr><td>Distance</td><td>%5</td></tr>"
0263         "</table>"
0264         "</body></html>");
0265 
0266     switch ( MarbleGlobal::getInstance()->locale()->measurementSystem() ) {
0267     case MarbleLocale::MetricSystem:
0268         //kilometers per hour
0269         unitString = QObject::tr("km/h");
0270         unitSpeed = speed * HOUR2SEC * METER2KM;
0271         altitudeUnitString = QObject::tr("m");
0272         distanceUnitString = QObject::tr("m");
0273         if ( length > 1000.0 ) {
0274             length /= 1000.0;
0275             distanceUnitString = QObject::tr("km");
0276         }
0277         altitude = position.altitude();
0278         break;
0279     case MarbleLocale::ImperialSystem:
0280         //miles per hour
0281         unitString = QObject::tr("m/h");
0282         unitSpeed = speed * HOUR2SEC * METER2KM * KM2MI;
0283         altitudeUnitString = QObject::tr("ft");
0284         distanceUnitString = QObject::tr("ft");
0285         altitude = position.altitude() * M2FT;
0286         length *= M2FT;
0287         break;
0288 
0289     case MarbleLocale::NauticalSystem:
0290         // nautical miles
0291         unitString = QObject::tr("kt");
0292         unitSpeed = speed * HOUR2SEC * METER2KM * KM2NM;
0293         altitudeUnitString = QObject::tr("m");
0294         distanceUnitString = QObject::tr("nm");
0295         altitude = position.altitude();
0296         length *= METER2KM*KM2NM;
0297         break;
0298     }
0299     // TODO read this value from the incoming signal
0300     const QString speedString = QLocale::system().toString( unitSpeed, 'f', 1);
0301     const QString altitudeString = QString( "%1 %2" ).arg( altitude, 0, 'f', 1, QChar(' ') ).arg( altitudeUnitString );
0302     const QString distanceString = QString( "%1 %2" ).arg( length, 0, 'f', 1, QChar(' ') ).arg( distanceUnitString );
0303 
0304     html = html.arg( position.lonToString(), position.latToString() );
0305     html = html.arg(altitudeString, speedString + QLatin1Char(' ') + unitString);
0306     html = html.arg( distanceString );
0307     m_currentLocationUi.locationLabel->setText( html );
0308     m_currentLocationUi.showTrackCheckBox->setEnabled( true );
0309     m_currentLocationUi.saveTrackButton->setEnabled( true );
0310     m_currentLocationUi.clearTrackButton->setEnabled( true );
0311 }
0312 
0313 void CurrentLocationWidgetPrivate::changePositionProvider( const QString &provider )
0314 {
0315     for( const PositionProviderPlugin* plugin: m_positionProviderPlugins ) {
0316         if ( plugin->guiString() == provider ) {
0317             m_currentLocationUi.locationLabel->setEnabled( true );
0318             PositionProviderPlugin* instance = plugin->newInstance();
0319             PositionTracking *tracking = m_widget->model()->positionTracking();
0320             tracking->setPositionProviderPlugin( instance );
0321             m_widget->update();
0322             return;
0323         }
0324     }
0325 
0326     // requested provider not found -> disable position tracking
0327     m_currentLocationUi.locationLabel->setEnabled( false );
0328     m_widget->model()->positionTracking()->setPositionProviderPlugin( nullptr );
0329     m_widget->update();
0330 }
0331 
0332 void CurrentLocationWidgetPrivate::trackPlacemark()
0333 {
0334     changePositionProvider( QObject::tr( "Placemark" ) );
0335     m_adjustNavigation->setRecenter( AutoNavigation::AlwaysRecenter );
0336 }
0337 
0338 void CurrentLocationWidget::setRecenterMode( int mode )
0339 {
0340     if ( mode >= 0 && mode <= AutoNavigation::RecenterOnBorder ) {
0341         AutoNavigation::CenterMode centerMode = ( AutoNavigation::CenterMode ) mode;
0342         d->m_adjustNavigation->setRecenter( centerMode );
0343     }
0344 }
0345 
0346 void CurrentLocationWidget::setAutoZoom( bool autoZoom )
0347 {
0348     d->m_adjustNavigation->setAutoZoom( autoZoom );
0349 }
0350 
0351 void CurrentLocationWidgetPrivate::updateAutoZoomCheckBox( bool autoZoom )
0352 {
0353     m_currentLocationUi.autoZoomCheckBox->setChecked( autoZoom );
0354 }
0355 
0356 void CurrentLocationWidgetPrivate::updateRecenterComboBox( AutoNavigation::CenterMode centerMode )
0357 {
0358     m_currentLocationUi.recenterComboBox->setCurrentIndex( centerMode );
0359 }
0360 
0361 void CurrentLocationWidgetPrivate::centerOnCurrentLocation()
0362 {
0363     m_widget->centerOn(m_currentPosition, true);
0364 }
0365 
0366 void CurrentLocationWidgetPrivate::saveTrack()
0367 {
0368     QString suggested = m_lastSavePath;
0369     QString fileName = QFileDialog::getSaveFileName(m_widget, QObject::tr("Save Track"), // krazy:exclude=qclasses
0370                                                     suggested.append(QLatin1Char('/') + QDateTime::currentDateTime().toString("yyyy-MM-dd_hhmmss") + QLatin1String(".kml")),
0371                             QObject::tr("KML File (*.kml)"));
0372     if ( fileName.isEmpty() ) {
0373         return;
0374     }
0375     if ( !fileName.endsWith(QLatin1String( ".kml" ), Qt::CaseInsensitive) ) {
0376         fileName += QLatin1String(".kml");
0377     }
0378     QFileInfo file( fileName );
0379     m_lastSavePath = file.absolutePath();
0380     m_widget->model()->positionTracking()->saveTrack( fileName );
0381 }
0382 
0383 void CurrentLocationWidgetPrivate::openTrack()
0384 {
0385     QString suggested = m_lastOpenPath;
0386     QString fileName = QFileDialog::getOpenFileName( m_widget, QObject::tr("Open Track"), // krazy:exclude=qclasses
0387                                                     suggested, QObject::tr("KML File (*.kml)"));
0388     if ( !fileName.isEmpty() ) {
0389         QFileInfo file( fileName );
0390         m_lastOpenPath = file.absolutePath();
0391         m_widget->model()->addGeoDataFile( fileName );
0392     }
0393 }
0394 
0395 void CurrentLocationWidgetPrivate::clearTrack()
0396 {
0397     const int result = QMessageBox::question( m_widget,
0398                                               QObject::tr( "Clear current track" ),
0399                                               QObject::tr( "Are you sure you want to clear the current track?" ),
0400                                               QMessageBox::Yes,
0401                                               QMessageBox::No );
0402 
0403     if ( result == QMessageBox::Yes ) {
0404         m_widget->model()->positionTracking()->clearTrack();
0405         m_widget->update();
0406         m_currentLocationUi.saveTrackButton->setEnabled( false );
0407         m_currentLocationUi.clearTrackButton->setEnabled( false );
0408     }
0409 }
0410 
0411 AutoNavigation::CenterMode CurrentLocationWidget::recenterMode() const
0412 {
0413     return d->m_adjustNavigation->recenterMode();
0414 }
0415 
0416 bool CurrentLocationWidget::autoZoom() const
0417 {
0418     return d->m_adjustNavigation->autoZoom();
0419 }
0420 
0421 bool CurrentLocationWidget::trackVisible() const
0422 {
0423     return d->m_widget->model()->positionTracking()->trackVisible();
0424 }
0425 
0426 QString CurrentLocationWidget::lastOpenPath() const
0427 {
0428     return d->m_lastOpenPath;
0429 }
0430 
0431 QString CurrentLocationWidget::lastSavePath() const
0432 {
0433     return d->m_lastSavePath;
0434 }
0435 
0436 void CurrentLocationWidget::setTrackVisible( bool visible )
0437 {
0438     d->m_currentLocationUi.showTrackCheckBox->setChecked( visible );
0439     d->m_widget->model()->positionTracking()->setTrackVisible( visible );
0440 }
0441 
0442 void CurrentLocationWidget::setLastOpenPath( const QString &path )
0443 {
0444     d->m_lastOpenPath = path;
0445 }
0446 
0447 void CurrentLocationWidget::setLastSavePath( const QString &path )
0448 {
0449     d->m_lastSavePath = path;
0450 }
0451 
0452 }
0453 
0454 #include "moc_CurrentLocationWidget.cpp"