File indexing completed on 2024-04-28 15:16:04
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"