Marble

PositionTracking.cpp
1// SPDX-License-Identifier: LGPL-2.1-or-later
2//
3// SPDX-FileCopyrightText: 2007 Andrew Manson <g.real.ate@gmail.com>
4// SPDX-FileCopyrightText: 2009 Eckhart Wörner <ewoerner@kde.org>
5// SPDX-FileCopyrightText: 2010 Thibaut Gridel <tgridel@free.fr>
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
31namespace Marble
32{
33
34class 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
73void 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
100void 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
116QString 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
134PositionTracking::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
175PositionTracking::~PositionTracking()
176{
177 d->m_treeModel->removeDocument( &d->m_document );
178 delete d;
179}
180
181void 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
213PositionProviderPlugin* PositionTracking::positionProviderPlugin()
214{
215 return d->m_positionProvider;
216}
217
218QString PositionTracking::error() const
219{
220 return d->m_positionProvider ? d->m_positionProvider->error() : QString();
221}
222
223
224//get speed from provider
225qreal PositionTracking::speed() const
226{
227 return d->m_positionProvider ? d->m_positionProvider->speed() : 0 ;
228}
229
230//get direction from provider
231qreal PositionTracking::direction() const
232{
233 return d->m_positionProvider ? d->m_positionProvider->direction() : 0 ;
234}
235
236QDateTime PositionTracking::timestamp() const
237{
238 return d->m_positionProvider ? d->m_positionProvider->timestamp() : QDateTime();
239}
240
241bool PositionTracking::trackVisible() const
242{
243 return d->m_currentTrackPlacemark->isVisible();
244}
245
246void PositionTracking::setTrackVisible( bool visible )
247{
248 d->m_currentTrackPlacemark->setVisible( visible );
249 d->m_treeModel->updateFeature( d->m_currentTrackPlacemark );
250}
251
252bool 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
278void 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
288void 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
354void PositionTracking::writeSettings()
355{
356 saveTrack( d->statusFile() );
357}
358
359bool 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
372qreal PositionTracking::length( qreal planetRadius ) const
373{
374 return d->m_length * planetRadius;
375}
376
377GeoDataAccuracy PositionTracking::accuracy() const
378{
379 return d->m_positionProvider ? d->m_positionProvider->accuracy() : GeoDataAccuracy();
380}
381
382GeoDataCoordinates PositionTracking::currentLocation() const
383{
384 return d->m_positionProvider ? d->m_positionProvider->position() : GeoDataCoordinates();
385}
386
387PositionProviderStatus PositionTracking::status() const
388{
389 return d->m_positionProvider ? d->m_positionProvider->status() : PositionProviderStatusUnavailable;
390}
391
392}
393
394#include "moc_PositionTracking.cpp"
qreal sphericalDistanceTo(const GeoDataCoordinates &other) const
This method calculates the shortest distance between two points on a sphere.
void setCoordinate(qreal longitude, qreal latitude, qreal altitude=0, GeoDataCoordinates::Unit _unit=GeoDataCoordinates::Radian)
Set the coordinate of the placemark in longitude and latitude.
int size() const
Returns the number of points in the track.
void addPoint(const QDateTime &when, const GeoDataCoordinates &coord)
Add a new point with coordinates coord associated with the time value when.
GeoDataCoordinates coordinatesAt(const QDateTime &when) const
If interpolate() is true, return the coordinates interpolated from the time values before and after w...
virtual qreal speed() const =0
Returns the speed of the gps device in meters per second.
Q_SCRIPTABLE CaptureState status()
KIOCORE_EXPORT QString dir(const QString &fileClass)
QString name(StandardShortcut id)
Binds a QML item to a specific geodetic location in screen coordinates.
void setAlpha(int alpha)
bool isEmpty() const const
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:18:17 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.