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

0001 // SPDX-License-Identifier: LGPL-2.1-or-later
0002 //
0003 // SPDX-FileCopyrightText: 2007 Andrew Manson <g.real.ate@gmail.com>
0004 // SPDX-FileCopyrightText: 2009 Eckhart Wörner <ewoerner@kde.org>
0005 // SPDX-FileCopyrightText: 2010 Thibaut Gridel <tgridel@free.fr>
0006 //
0007 
0008 #include "PositionTracking.h"
0009 
0010 #include "GeoDataDocument.h"
0011 #include "GeoDataMultiTrack.h"
0012 #include "GeoDataPlacemark.h"
0013 #include "GeoDataParser.h"
0014 #include "GeoDataStyle.h"
0015 #include "GeoDataLineStyle.h"
0016 #include "GeoDataStyleMap.h"
0017 #include "GeoDataTrack.h"
0018 #include "GeoDataTreeModel.h"
0019 #include "GeoDataLineString.h"
0020 #include "GeoDataAccuracy.h"
0021 #include "GeoDataDocumentWriter.h"
0022 #include "KmlElementDictionary.h"
0023 #include "FileManager.h"
0024 #include "MarbleColors.h"
0025 #include "MarbleDebug.h"
0026 #include "MarbleDirs.h"
0027 #include "PositionProviderPlugin.h"
0028 
0029 #include <QFile>
0030 
0031 namespace Marble
0032 {
0033 
0034 class PositionTrackingPrivate
0035 {
0036  public:
0037     PositionTrackingPrivate( GeoDataTreeModel *model, PositionTracking *parent ) :
0038         q( parent ),
0039         m_treeModel( model ),
0040         m_currentPositionPlacemark( new GeoDataPlacemark ),
0041         m_currentTrackPlacemark( new GeoDataPlacemark ),
0042         m_trackSegments( new GeoDataMultiTrack ),
0043         m_document(),
0044         m_currentTrack( nullptr ),
0045         m_positionProvider( nullptr ),
0046         m_length( 0.0 )
0047     {
0048     }
0049 
0050     void updatePosition();
0051 
0052     void updateStatus();
0053 
0054     static QString statusFile();
0055 
0056     PositionTracking *const q;
0057 
0058     GeoDataTreeModel *const m_treeModel;
0059 
0060     GeoDataPlacemark *const m_currentPositionPlacemark;
0061     GeoDataPlacemark *m_currentTrackPlacemark;
0062     GeoDataMultiTrack *m_trackSegments;
0063     GeoDataDocument m_document;
0064 
0065     GeoDataCoordinates  m_gpsPreviousPosition;
0066     GeoDataTrack  *m_currentTrack;
0067 
0068     PositionProviderPlugin* m_positionProvider;
0069 
0070     qreal m_length;
0071 };
0072 
0073 void PositionTrackingPrivate::updatePosition()
0074 {
0075     Q_ASSERT( m_positionProvider != nullptr );
0076 
0077     const GeoDataAccuracy accuracy = m_positionProvider->accuracy();
0078     const GeoDataCoordinates position = m_positionProvider->position();
0079     const QDateTime timestamp = m_positionProvider->timestamp();
0080 
0081     if ( m_positionProvider->status() == PositionProviderStatusAvailable ) {
0082         if ( accuracy.horizontal < 250 ) {
0083             if ( m_currentTrack->size() ) {
0084                 m_length += m_currentTrack->coordinatesAt(m_currentTrack->size() - 1).sphericalDistanceTo(position);
0085             }
0086             m_currentTrack->addPoint( timestamp, position );
0087         }
0088 
0089         //if the position has moved then update the current position
0090         if ( m_gpsPreviousPosition != position ) {
0091             m_currentPositionPlacemark->setCoordinate( position );
0092 
0093             qreal speed = m_positionProvider->speed();
0094             emit q->gpsLocation( position, speed );
0095         }
0096     }
0097 }
0098 
0099 
0100 void PositionTrackingPrivate::updateStatus()
0101 {
0102     Q_ASSERT( m_positionProvider != nullptr );
0103 
0104     const PositionProviderStatus status = m_positionProvider->status();
0105 
0106     if (status == PositionProviderStatusAvailable) {
0107         m_currentTrack = new GeoDataTrack;
0108         m_treeModel->removeFeature( m_currentTrackPlacemark );
0109         m_trackSegments->append( m_currentTrack );
0110         m_treeModel->addFeature( &m_document, m_currentTrackPlacemark );
0111     }
0112 
0113     emit q->statusChanged( status );
0114 }
0115 
0116 QString PositionTrackingPrivate::statusFile()
0117 {
0118     QString const subdir = "tracking";
0119     QDir dir( MarbleDirs::localPath() );
0120     if ( !dir.exists( subdir ) ) {
0121         if ( !dir.mkdir( subdir ) ) {
0122             mDebug() << "Unable to create dir " << dir.absoluteFilePath( subdir );
0123             return dir.absolutePath();
0124         }
0125     }
0126 
0127     if ( !dir.cd( subdir ) ) {
0128         mDebug() << "Cannot change into " << dir.absoluteFilePath( subdir );
0129     }
0130 
0131     return dir.absoluteFilePath( "track.kml" );
0132 }
0133 
0134 PositionTracking::PositionTracking( GeoDataTreeModel *model )
0135      : QObject( model ),
0136        d( new PositionTrackingPrivate( model, this ) )
0137 {
0138     d->m_document.setDocumentRole( TrackingDocument );
0139     d->m_document.setName(QStringLiteral("Position Tracking"));
0140 
0141     // First point is current position
0142     d->m_currentPositionPlacemark->setName(QStringLiteral("Current Position"));
0143     d->m_currentPositionPlacemark->setVisible(false);
0144     d->m_document.append( d->m_currentPositionPlacemark );
0145 
0146     // Second point is position track
0147     d->m_currentTrack = new GeoDataTrack;
0148     d->m_trackSegments->append(d->m_currentTrack);
0149 
0150     d->m_currentTrackPlacemark->setGeometry(d->m_trackSegments);
0151     d->m_currentTrackPlacemark->setName(QStringLiteral("Current Track"));
0152 
0153     GeoDataStyle::Ptr style(new GeoDataStyle);
0154     GeoDataLineStyle lineStyle;
0155     QColor transparentRed = Oxygen::brickRed4;
0156     transparentRed.setAlpha( 200 );
0157     lineStyle.setColor( transparentRed );
0158     lineStyle.setWidth( 4 );
0159     style->setLineStyle(lineStyle);
0160     style->setId(QStringLiteral("track"));
0161 
0162     GeoDataStyleMap styleMap;
0163     styleMap.setId(QStringLiteral("map-track"));
0164     styleMap.insert(QStringLiteral("normal"), QLatin1Char('#') + style->id());
0165     d->m_document.addStyleMap(styleMap);
0166     d->m_document.addStyle(style);
0167     d->m_document.append( d->m_currentTrackPlacemark );
0168 
0169     d->m_currentTrackPlacemark->setStyleUrl(QLatin1Char('#') + styleMap.id());
0170 
0171     d->m_treeModel->addDocument( &d->m_document );
0172 }
0173 
0174 
0175 PositionTracking::~PositionTracking()
0176 {
0177     d->m_treeModel->removeDocument( &d->m_document );
0178     delete d;
0179 }
0180 
0181 void PositionTracking::setPositionProviderPlugin( PositionProviderPlugin* plugin )
0182 {
0183     const PositionProviderStatus oldStatus = status();
0184 
0185     if ( d->m_positionProvider ) {
0186         delete d->m_positionProvider;
0187     }
0188 
0189     d->m_positionProvider = plugin;
0190 
0191     if ( d->m_positionProvider ) {
0192         d->m_positionProvider->setParent( this );
0193         mDebug() << "Initializing position provider:" << d->m_positionProvider->name();
0194         connect( d->m_positionProvider, SIGNAL(statusChanged(PositionProviderStatus)),
0195                 this, SLOT(updateStatus()) );
0196         connect( d->m_positionProvider, SIGNAL(positionChanged(GeoDataCoordinates,GeoDataAccuracy)),
0197                  this, SLOT(updatePosition()) );
0198 
0199         d->m_positionProvider->initialize();
0200     }
0201 
0202     emit positionProviderPluginChanged( plugin );
0203 
0204     if ( oldStatus != status() ) {
0205         emit statusChanged( status() );
0206     }
0207 
0208     if ( status() == PositionProviderStatusAvailable ) {
0209         emit gpsLocation( d->m_positionProvider->position(), d->m_positionProvider->speed() );
0210     }
0211 }
0212 
0213 PositionProviderPlugin* PositionTracking::positionProviderPlugin()
0214 {
0215     return d->m_positionProvider;
0216 }
0217 
0218 QString PositionTracking::error() const
0219 {
0220     return d->m_positionProvider ? d->m_positionProvider->error() : QString();
0221 }
0222 
0223 
0224 //get speed from provider
0225 qreal PositionTracking::speed() const
0226 {
0227     return d->m_positionProvider ? d->m_positionProvider->speed() : 0 ;
0228 }
0229 
0230 //get direction from provider
0231 qreal PositionTracking::direction() const
0232 {
0233     return d->m_positionProvider ? d->m_positionProvider->direction() : 0 ;
0234 }
0235 
0236 QDateTime PositionTracking::timestamp() const
0237 {
0238     return d->m_positionProvider ? d->m_positionProvider->timestamp() : QDateTime();
0239 }
0240 
0241 bool PositionTracking::trackVisible() const
0242 {
0243     return d->m_currentTrackPlacemark->isVisible();
0244 }
0245 
0246 void PositionTracking::setTrackVisible( bool visible )
0247 {
0248     d->m_currentTrackPlacemark->setVisible( visible );
0249     d->m_treeModel->updateFeature( d->m_currentTrackPlacemark );
0250 }
0251 
0252 bool PositionTracking::saveTrack( const QString& fileName )
0253 {
0254 
0255     if ( fileName.isEmpty() ) {
0256         return false;
0257     }
0258 
0259     GeoDataDocument *document = new GeoDataDocument;
0260     QFileInfo fileInfo( fileName );
0261     QString name = fileInfo.baseName();
0262     document->setName( name );
0263     for( const GeoDataStyle::Ptr &style: d->m_document.styles() ) {
0264         document->addStyle( style );
0265     }
0266     for( const GeoDataStyleMap &map: d->m_document.styleMaps() ) {
0267         document->addStyleMap( map );
0268     }
0269     GeoDataPlacemark *track = new GeoDataPlacemark( *d->m_currentTrackPlacemark );
0270     track->setName(QLatin1String("Track ") + name);
0271     document->append( track );
0272 
0273     bool const result = GeoDataDocumentWriter::write(fileName, *document);
0274     delete document;
0275     return result;
0276 }
0277 
0278 void PositionTracking::clearTrack()
0279 {
0280     d->m_treeModel->removeFeature( d->m_currentTrackPlacemark );
0281     d->m_currentTrack = new GeoDataTrack;
0282     d->m_trackSegments->clear();
0283     d->m_trackSegments->append( d->m_currentTrack );
0284     d->m_treeModel->addFeature( &d->m_document, d->m_currentTrackPlacemark );
0285     d->m_length = 0.0;
0286 }
0287 
0288 void PositionTracking::readSettings()
0289 {
0290     QFile file( d->statusFile() );
0291     if ( !file.open( QIODevice::ReadOnly ) ) {
0292         mDebug() << "Can not read track from " << file.fileName();
0293         return;
0294     }
0295 
0296     GeoDataParser parser( GeoData_KML );
0297     if ( !parser.read( &file ) ) {
0298         mDebug() << "Could not parse tracking file: " << parser.errorString();
0299         return;
0300     }
0301 
0302     GeoDataDocument *doc = dynamic_cast<GeoDataDocument*>( parser.releaseDocument() );
0303     file.close();
0304 
0305     if( !doc ){
0306         mDebug() << "tracking document not available";
0307         return;
0308     }
0309 
0310     GeoDataPlacemark *track = dynamic_cast<GeoDataPlacemark*>( doc->child( 0 ) );
0311     if( !track ) {
0312         mDebug() << "tracking document doesn't have a placemark";
0313         delete doc;
0314         return;
0315     }
0316 
0317     d->m_trackSegments = dynamic_cast<GeoDataMultiTrack*>( track->geometry() );
0318     if( !d->m_trackSegments ) {
0319         mDebug() << "tracking document doesn't have a multitrack";
0320         delete doc;
0321         return;
0322     }
0323     if( d->m_trackSegments->size() < 1 ) {
0324         mDebug() << "tracking document doesn't have a track";
0325         delete doc;
0326         return;
0327     }
0328 
0329     d->m_currentTrack = dynamic_cast<GeoDataTrack*>( d->m_trackSegments->child( d->m_trackSegments->size() - 1 ) );
0330     if( !d->m_currentTrack ) {
0331         mDebug() << "tracking document doesn't have a last track";
0332         delete doc;
0333         return;
0334     }
0335 
0336     doc->remove( 0 );
0337     delete doc;
0338 
0339     d->m_treeModel->removeDocument( &d->m_document );
0340     d->m_document.remove( 1 );
0341     delete d->m_currentTrackPlacemark;
0342     d->m_currentTrackPlacemark = track;
0343     d->m_currentTrackPlacemark->setName(QStringLiteral("Current Track"));
0344     d->m_document.append( d->m_currentTrackPlacemark );
0345     d->m_currentTrackPlacemark->setStyleUrl( d->m_currentTrackPlacemark->styleUrl() );
0346 
0347     d->m_treeModel->addDocument( &d->m_document );
0348     d->m_length = 0.0;
0349     for ( int i = 0; i < d->m_trackSegments->size(); ++i ) {
0350         d->m_length += d->m_trackSegments->at( i ).lineString()->length( 1 );
0351     }
0352 }
0353 
0354 void PositionTracking::writeSettings()
0355 {
0356     saveTrack( d->statusFile() );
0357 }
0358 
0359 bool PositionTracking::isTrackEmpty() const
0360 {
0361     if ( d->m_trackSegments->size() < 1 ) {
0362         return true;
0363     }
0364 
0365     if ( d->m_trackSegments->size() == 1 ) {
0366         return ( d->m_currentTrack->size() == 0 );
0367     }
0368 
0369     return false;
0370 }
0371 
0372 qreal PositionTracking::length( qreal planetRadius ) const
0373 {
0374     return d->m_length * planetRadius;
0375 }
0376 
0377 GeoDataAccuracy PositionTracking::accuracy() const
0378 {
0379     return d->m_positionProvider ? d->m_positionProvider->accuracy() : GeoDataAccuracy();
0380 }
0381 
0382 GeoDataCoordinates PositionTracking::currentLocation() const
0383 {
0384     return d->m_positionProvider ? d->m_positionProvider->position() : GeoDataCoordinates();
0385 }
0386 
0387 PositionProviderStatus PositionTracking::status() const
0388 {
0389     return d->m_positionProvider ? d->m_positionProvider->status() : PositionProviderStatusUnavailable;
0390 }
0391 
0392 }
0393 
0394 #include "moc_PositionTracking.cpp"