Marble

PositionTracking.cpp
1 // SPDX-License-Identifier: LGPL-2.1-or-later
2 //
3 // SPDX-FileCopyrightText: 2007 Andrew Manson <[email protected]>
4 // SPDX-FileCopyrightText: 2009 Eckhart Wörner <[email protected]>
5 // SPDX-FileCopyrightText: 2010 Thibaut Gridel <[email protected]>
6 //
7 
8 #include "PositionTracking.h"
9 
10 #include "GeoDataDocument.h"
11 #include "GeoDataMultiTrack.h"
12 #include "GeoDataPlacemark.h"
13 #include "GeoDataParser.h"
14 #include "GeoDataStyle.h"
15 #include "GeoDataLineStyle.h"
16 #include "GeoDataStyleMap.h"
17 #include "GeoDataTrack.h"
18 #include "GeoDataTreeModel.h"
19 #include "GeoDataLineString.h"
20 #include "GeoDataAccuracy.h"
21 #include "GeoDataDocumentWriter.h"
22 #include "KmlElementDictionary.h"
23 #include "FileManager.h"
24 #include "MarbleColors.h"
25 #include "MarbleDebug.h"
26 #include "MarbleDirs.h"
27 #include "PositionProviderPlugin.h"
28 
29 #include <QFile>
30 
31 namespace Marble
32 {
33 
34 class PositionTrackingPrivate
35 {
36  public:
37  PositionTrackingPrivate( GeoDataTreeModel *model, PositionTracking *parent ) :
38  q( parent ),
39  m_treeModel( model ),
40  m_currentPositionPlacemark( new GeoDataPlacemark ),
41  m_currentTrackPlacemark( new GeoDataPlacemark ),
42  m_trackSegments( new GeoDataMultiTrack ),
43  m_document(),
44  m_currentTrack( nullptr ),
45  m_positionProvider( nullptr ),
46  m_length( 0.0 )
47  {
48  }
49 
50  void updatePosition();
51 
52  void updateStatus();
53 
54  static QString statusFile();
55 
56  PositionTracking *const q;
57 
58  GeoDataTreeModel *const m_treeModel;
59 
60  GeoDataPlacemark *const m_currentPositionPlacemark;
61  GeoDataPlacemark *m_currentTrackPlacemark;
62  GeoDataMultiTrack *m_trackSegments;
63  GeoDataDocument m_document;
64 
65  GeoDataCoordinates m_gpsPreviousPosition;
66  GeoDataTrack *m_currentTrack;
67 
68  PositionProviderPlugin* m_positionProvider;
69 
70  qreal m_length;
71 };
72 
73 void PositionTrackingPrivate::updatePosition()
74 {
75  Q_ASSERT( m_positionProvider != nullptr );
76 
77  const GeoDataAccuracy accuracy = m_positionProvider->accuracy();
78  const GeoDataCoordinates position = m_positionProvider->position();
79  const QDateTime timestamp = m_positionProvider->timestamp();
80 
81  if ( m_positionProvider->status() == PositionProviderStatusAvailable ) {
82  if ( accuracy.horizontal < 250 ) {
83  if ( m_currentTrack->size() ) {
84  m_length += m_currentTrack->coordinatesAt(m_currentTrack->size() - 1).sphericalDistanceTo(position);
85  }
86  m_currentTrack->addPoint( timestamp, position );
87  }
88 
89  //if the position has moved then update the current position
90  if ( m_gpsPreviousPosition != position ) {
91  m_currentPositionPlacemark->setCoordinate( position );
92 
93  qreal speed = m_positionProvider->speed();
94  emit q->gpsLocation( position, speed );
95  }
96  }
97 }
98 
99 
100 void PositionTrackingPrivate::updateStatus()
101 {
102  Q_ASSERT( m_positionProvider != nullptr );
103 
104  const PositionProviderStatus status = m_positionProvider->status();
105 
106  if (status == PositionProviderStatusAvailable) {
107  m_currentTrack = new GeoDataTrack;
108  m_treeModel->removeFeature( m_currentTrackPlacemark );
109  m_trackSegments->append( m_currentTrack );
110  m_treeModel->addFeature( &m_document, m_currentTrackPlacemark );
111  }
112 
113  emit q->statusChanged( status );
114 }
115 
116 QString PositionTrackingPrivate::statusFile()
117 {
118  QString const subdir = "tracking";
119  QDir dir( MarbleDirs::localPath() );
120  if ( !dir.exists( subdir ) ) {
121  if ( !dir.mkdir( subdir ) ) {
122  mDebug() << "Unable to create dir " << dir.absoluteFilePath( subdir );
123  return dir.absolutePath();
124  }
125  }
126 
127  if ( !dir.cd( subdir ) ) {
128  mDebug() << "Cannot change into " << dir.absoluteFilePath( subdir );
129  }
130 
131  return dir.absoluteFilePath( "track.kml" );
132 }
133 
134 PositionTracking::PositionTracking( GeoDataTreeModel *model )
135  : QObject( model ),
136  d( new PositionTrackingPrivate( model, this ) )
137 {
138  d->m_document.setDocumentRole( TrackingDocument );
139  d->m_document.setName(QStringLiteral("Position Tracking"));
140 
141  // First point is current position
142  d->m_currentPositionPlacemark->setName(QStringLiteral("Current Position"));
143  d->m_currentPositionPlacemark->setVisible(false);
144  d->m_document.append( d->m_currentPositionPlacemark );
145 
146  // Second point is position track
147  d->m_currentTrack = new GeoDataTrack;
148  d->m_trackSegments->append(d->m_currentTrack);
149 
150  d->m_currentTrackPlacemark->setGeometry(d->m_trackSegments);
151  d->m_currentTrackPlacemark->setName(QStringLiteral("Current Track"));
152 
153  GeoDataStyle::Ptr style(new GeoDataStyle);
154  GeoDataLineStyle lineStyle;
155  QColor transparentRed = Oxygen::brickRed4;
156  transparentRed.setAlpha( 200 );
157  lineStyle.setColor( transparentRed );
158  lineStyle.setWidth( 4 );
159  style->setLineStyle(lineStyle);
160  style->setId(QStringLiteral("track"));
161 
162  GeoDataStyleMap styleMap;
163  styleMap.setId(QStringLiteral("map-track"));
164  styleMap.insert(QStringLiteral("normal"), QLatin1Char('#') + style->id());
165  d->m_document.addStyleMap(styleMap);
166  d->m_document.addStyle(style);
167  d->m_document.append( d->m_currentTrackPlacemark );
168 
169  d->m_currentTrackPlacemark->setStyleUrl(QLatin1Char('#') + styleMap.id());
170 
171  d->m_treeModel->addDocument( &d->m_document );
172 }
173 
174 
175 PositionTracking::~PositionTracking()
176 {
177  d->m_treeModel->removeDocument( &d->m_document );
178  delete d;
179 }
180 
181 void PositionTracking::setPositionProviderPlugin( PositionProviderPlugin* plugin )
182 {
183  const PositionProviderStatus oldStatus = status();
184 
185  if ( d->m_positionProvider ) {
186  delete d->m_positionProvider;
187  }
188 
189  d->m_positionProvider = plugin;
190 
191  if ( d->m_positionProvider ) {
192  d->m_positionProvider->setParent( this );
193  mDebug() << "Initializing position provider:" << d->m_positionProvider->name();
194  connect( d->m_positionProvider, SIGNAL(statusChanged(PositionProviderStatus)),
195  this, SLOT(updateStatus()) );
196  connect( d->m_positionProvider, SIGNAL(positionChanged(GeoDataCoordinates,GeoDataAccuracy)),
197  this, SLOT(updatePosition()) );
198 
199  d->m_positionProvider->initialize();
200  }
201 
202  emit positionProviderPluginChanged( plugin );
203 
204  if ( oldStatus != status() ) {
205  emit statusChanged( status() );
206  }
207 
208  if ( status() == PositionProviderStatusAvailable ) {
209  emit gpsLocation( d->m_positionProvider->position(), d->m_positionProvider->speed() );
210  }
211 }
212 
213 PositionProviderPlugin* PositionTracking::positionProviderPlugin()
214 {
215  return d->m_positionProvider;
216 }
217 
218 QString PositionTracking::error() const
219 {
220  return d->m_positionProvider ? d->m_positionProvider->error() : QString();
221 }
222 
223 
224 //get speed from provider
225 qreal PositionTracking::speed() const
226 {
227  return d->m_positionProvider ? d->m_positionProvider->speed() : 0 ;
228 }
229 
230 //get direction from provider
231 qreal PositionTracking::direction() const
232 {
233  return d->m_positionProvider ? d->m_positionProvider->direction() : 0 ;
234 }
235 
236 QDateTime PositionTracking::timestamp() const
237 {
238  return d->m_positionProvider ? d->m_positionProvider->timestamp() : QDateTime();
239 }
240 
241 bool PositionTracking::trackVisible() const
242 {
243  return d->m_currentTrackPlacemark->isVisible();
244 }
245 
246 void PositionTracking::setTrackVisible( bool visible )
247 {
248  d->m_currentTrackPlacemark->setVisible( visible );
249  d->m_treeModel->updateFeature( d->m_currentTrackPlacemark );
250 }
251 
252 bool PositionTracking::saveTrack( const QString& fileName )
253 {
254 
255  if ( fileName.isEmpty() ) {
256  return false;
257  }
258 
259  GeoDataDocument *document = new GeoDataDocument;
260  QFileInfo fileInfo( fileName );
261  QString name = fileInfo.baseName();
262  document->setName( name );
263  for( const GeoDataStyle::Ptr &style: d->m_document.styles() ) {
264  document->addStyle( style );
265  }
266  for( const GeoDataStyleMap &map: d->m_document.styleMaps() ) {
267  document->addStyleMap( map );
268  }
269  GeoDataPlacemark *track = new GeoDataPlacemark( *d->m_currentTrackPlacemark );
270  track->setName(QLatin1String("Track ") + name);
271  document->append( track );
272 
273  bool const result = GeoDataDocumentWriter::write(fileName, *document);
274  delete document;
275  return result;
276 }
277 
278 void PositionTracking::clearTrack()
279 {
280  d->m_treeModel->removeFeature( d->m_currentTrackPlacemark );
281  d->m_currentTrack = new GeoDataTrack;
282  d->m_trackSegments->clear();
283  d->m_trackSegments->append( d->m_currentTrack );
284  d->m_treeModel->addFeature( &d->m_document, d->m_currentTrackPlacemark );
285  d->m_length = 0.0;
286 }
287 
288 void PositionTracking::readSettings()
289 {
290  QFile file( d->statusFile() );
291  if ( !file.open( QIODevice::ReadOnly ) ) {
292  mDebug() << "Can not read track from " << file.fileName();
293  return;
294  }
295 
296  GeoDataParser parser( GeoData_KML );
297  if ( !parser.read( &file ) ) {
298  mDebug() << "Could not parse tracking file: " << parser.errorString();
299  return;
300  }
301 
302  GeoDataDocument *doc = dynamic_cast<GeoDataDocument*>( parser.releaseDocument() );
303  file.close();
304 
305  if( !doc ){
306  mDebug() << "tracking document not available";
307  return;
308  }
309 
310  GeoDataPlacemark *track = dynamic_cast<GeoDataPlacemark*>( doc->child( 0 ) );
311  if( !track ) {
312  mDebug() << "tracking document doesn't have a placemark";
313  delete doc;
314  return;
315  }
316 
317  d->m_trackSegments = dynamic_cast<GeoDataMultiTrack*>( track->geometry() );
318  if( !d->m_trackSegments ) {
319  mDebug() << "tracking document doesn't have a multitrack";
320  delete doc;
321  return;
322  }
323  if( d->m_trackSegments->size() < 1 ) {
324  mDebug() << "tracking document doesn't have a track";
325  delete doc;
326  return;
327  }
328 
329  d->m_currentTrack = dynamic_cast<GeoDataTrack*>( d->m_trackSegments->child( d->m_trackSegments->size() - 1 ) );
330  if( !d->m_currentTrack ) {
331  mDebug() << "tracking document doesn't have a last track";
332  delete doc;
333  return;
334  }
335 
336  doc->remove( 0 );
337  delete doc;
338 
339  d->m_treeModel->removeDocument( &d->m_document );
340  d->m_document.remove( 1 );
341  delete d->m_currentTrackPlacemark;
342  d->m_currentTrackPlacemark = track;
343  d->m_currentTrackPlacemark->setName(QStringLiteral("Current Track"));
344  d->m_document.append( d->m_currentTrackPlacemark );
345  d->m_currentTrackPlacemark->setStyleUrl( d->m_currentTrackPlacemark->styleUrl() );
346 
347  d->m_treeModel->addDocument( &d->m_document );
348  d->m_length = 0.0;
349  for ( int i = 0; i < d->m_trackSegments->size(); ++i ) {
350  d->m_length += d->m_trackSegments->at( i ).lineString()->length( 1 );
351  }
352 }
353 
354 void PositionTracking::writeSettings()
355 {
356  saveTrack( d->statusFile() );
357 }
358 
359 bool PositionTracking::isTrackEmpty() const
360 {
361  if ( d->m_trackSegments->size() < 1 ) {
362  return true;
363  }
364 
365  if ( d->m_trackSegments->size() == 1 ) {
366  return ( d->m_currentTrack->size() == 0 );
367  }
368 
369  return false;
370 }
371 
372 qreal PositionTracking::length( qreal planetRadius ) const
373 {
374  return d->m_length * planetRadius;
375 }
376 
377 GeoDataAccuracy PositionTracking::accuracy() const
378 {
379  return d->m_positionProvider ? d->m_positionProvider->accuracy() : GeoDataAccuracy();
380 }
381 
382 GeoDataCoordinates PositionTracking::currentLocation() const
383 {
384  return d->m_positionProvider ? d->m_positionProvider->position() : GeoDataCoordinates();
385 }
386 
387 PositionProviderStatus PositionTracking::status() const
388 {
389  return d->m_positionProvider ? d->m_positionProvider->status() : PositionProviderStatusUnavailable;
390 }
391 
392 }
393 
394 #include "moc_PositionTracking.cpp"
static bool write(QIODevice *device, const GeoDataDocument &document, const QString &documentIdentifier)
Write the content of the given GeoDataDocument to the given I/O device.
bool isEmpty() const const
Q_SCRIPTABLE CaptureState status()
void setAlpha(int alpha)
Binds a QML item to a specific geodetic location in screen coordinates.
KIOFILEWIDGETS_EXPORT QString dir(const QString &fileClass)
const char * name(StandardAction id)
QDebug mDebug()
a function to replace qDebug() in Marble library code
Definition: MarbleDebug.cpp:31
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Wed Oct 4 2023 04:09:43 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.