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 "FileManager.h"
11#include "GeoDataAccuracy.h"
12#include "GeoDataDocument.h"
13#include "GeoDataDocumentWriter.h"
14#include "GeoDataLineString.h"
15#include "GeoDataLineStyle.h"
16#include "GeoDataMultiTrack.h"
17#include "GeoDataParser.h"
18#include "GeoDataPlacemark.h"
19#include "GeoDataStyle.h"
20#include "GeoDataStyleMap.h"
21#include "GeoDataTrack.h"
22#include "GeoDataTreeModel.h"
23#include "KmlElementDictionary.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{
36public:
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 Q_EMIT q->gpsLocation(position, speed);
95 }
96 }
97}
98
99void PositionTrackingPrivate::updateStatus()
100{
101 Q_ASSERT(m_positionProvider != nullptr);
102
103 const PositionProviderStatus status = m_positionProvider->status();
104
105 if (status == PositionProviderStatusAvailable) {
106 m_currentTrack = new GeoDataTrack;
107 m_treeModel->removeFeature(m_currentTrackPlacemark);
108 m_trackSegments->append(m_currentTrack);
109 m_treeModel->addFeature(&m_document, m_currentTrackPlacemark);
110 }
111
112 Q_EMIT q->statusChanged(status);
113}
114
115QString PositionTrackingPrivate::statusFile()
116{
117 QString const subdir = QStringLiteral("tracking");
118 QDir dir(MarbleDirs::localPath());
119 if (!dir.exists(subdir)) {
120 if (!dir.mkdir(subdir)) {
121 mDebug() << "Unable to create dir " << dir.absoluteFilePath(subdir);
122 return dir.absolutePath();
123 }
124 }
125
126 if (!dir.cd(subdir)) {
127 mDebug() << "Cannot change into " << dir.absoluteFilePath(subdir);
128 }
129
130 return dir.absoluteFilePath(QStringLiteral("track.kml"));
131}
132
133PositionTracking::PositionTracking(GeoDataTreeModel *model)
134 : QObject(model)
135 , d(new PositionTrackingPrivate(model, this))
136{
137 d->m_document.setDocumentRole(TrackingDocument);
138 d->m_document.setName(QStringLiteral("Position Tracking"));
139
140 // First point is current position
141 d->m_currentPositionPlacemark->setName(QStringLiteral("Current Position"));
142 d->m_currentPositionPlacemark->setVisible(false);
143 d->m_document.append(d->m_currentPositionPlacemark);
144
145 // Second point is position track
146 d->m_currentTrack = new GeoDataTrack;
147 d->m_trackSegments->append(d->m_currentTrack);
148
149 d->m_currentTrackPlacemark->setGeometry(d->m_trackSegments);
150 d->m_currentTrackPlacemark->setName(QStringLiteral("Current Track"));
151
152 GeoDataStyle::Ptr style(new GeoDataStyle);
153 GeoDataLineStyle lineStyle;
154 QColor transparentRed = Oxygen::brickRed4;
155 transparentRed.setAlpha(200);
156 lineStyle.setColor(transparentRed);
157 lineStyle.setWidth(4);
158 style->setLineStyle(lineStyle);
159 style->setId(QStringLiteral("track"));
160
161 GeoDataStyleMap styleMap;
162 styleMap.setId(QStringLiteral("map-track"));
163 styleMap.insert(QStringLiteral("normal"), QLatin1Char('#') + style->id());
164 d->m_document.addStyleMap(styleMap);
165 d->m_document.addStyle(style);
166 d->m_document.append(d->m_currentTrackPlacemark);
167
168 d->m_currentTrackPlacemark->setStyleUrl(QLatin1Char('#') + styleMap.id());
169
170 d->m_treeModel->addDocument(&d->m_document);
171}
172
173PositionTracking::~PositionTracking()
174{
175 d->m_treeModel->removeDocument(&d->m_document);
176 delete d;
177}
178
179void PositionTracking::setPositionProviderPlugin(PositionProviderPlugin *plugin)
180{
181 const PositionProviderStatus oldStatus = status();
182
183 if (d->m_positionProvider) {
184 delete d->m_positionProvider;
185 }
186
187 d->m_positionProvider = plugin;
188
189 if (d->m_positionProvider) {
190 d->m_positionProvider->setParent(this);
191 mDebug() << "Initializing position provider:" << d->m_positionProvider->name();
192 connect(d->m_positionProvider, SIGNAL(statusChanged(PositionProviderStatus)), this, SLOT(updateStatus()));
193 connect(d->m_positionProvider, SIGNAL(positionChanged(GeoDataCoordinates, GeoDataAccuracy)), this, SLOT(updatePosition()));
194
195 d->m_positionProvider->initialize();
196 }
197
198 Q_EMIT positionProviderPluginChanged(plugin);
199
200 if (oldStatus != status()) {
201 Q_EMIT statusChanged(status());
202 }
203
204 if (status() == PositionProviderStatusAvailable) {
205 Q_EMIT gpsLocation(d->m_positionProvider->position(), d->m_positionProvider->speed());
206 }
207}
208
209PositionProviderPlugin *PositionTracking::positionProviderPlugin()
210{
211 return d->m_positionProvider;
212}
213
214QString PositionTracking::error() const
215{
216 return d->m_positionProvider ? d->m_positionProvider->error() : QString();
217}
218
219// get speed from provider
220qreal PositionTracking::speed() const
221{
222 return d->m_positionProvider ? d->m_positionProvider->speed() : 0;
223}
224
225// get direction from provider
226qreal PositionTracking::direction() const
227{
228 return d->m_positionProvider ? d->m_positionProvider->direction() : 0;
229}
230
231QDateTime PositionTracking::timestamp() const
232{
233 return d->m_positionProvider ? d->m_positionProvider->timestamp() : QDateTime();
234}
235
236bool PositionTracking::trackVisible() const
237{
238 return d->m_currentTrackPlacemark->isVisible();
239}
240
241void PositionTracking::setTrackVisible(bool visible)
242{
243 d->m_currentTrackPlacemark->setVisible(visible);
244 d->m_treeModel->updateFeature(d->m_currentTrackPlacemark);
245}
246
247bool PositionTracking::saveTrack(const QString &fileName)
248{
249 if (fileName.isEmpty()) {
250 return false;
251 }
252
253 auto document = new GeoDataDocument;
254 QFileInfo fileInfo(fileName);
255 QString name = fileInfo.baseName();
256 document->setName(name);
257 const auto styles = d->m_document.styles();
258 for (const GeoDataStyle::Ptr &style : styles) {
259 document->addStyle(style);
260 }
261 const auto styleMaps = d->m_document.styleMaps();
262 for (const GeoDataStyleMap &map : styleMaps) {
263 document->addStyleMap(map);
264 }
265 auto track = new GeoDataPlacemark(*d->m_currentTrackPlacemark);
266 track->setName(QLatin1StringView("Track ") + name);
267 document->append(track);
268
269 bool const result = GeoDataDocumentWriter::write(fileName, *document);
270 delete document;
271 return result;
272}
273
274void PositionTracking::clearTrack()
275{
276 d->m_treeModel->removeFeature(d->m_currentTrackPlacemark);
277 d->m_currentTrack = new GeoDataTrack;
278 d->m_trackSegments->clear();
279 d->m_trackSegments->append(d->m_currentTrack);
280 d->m_treeModel->addFeature(&d->m_document, d->m_currentTrackPlacemark);
281 d->m_length = 0.0;
282}
283
284void PositionTracking::readSettings()
285{
286 QFile file(d->statusFile());
287 if (!file.open(QIODevice::ReadOnly)) {
288 mDebug() << "Can not read track from " << file.fileName();
289 return;
290 }
291
292 GeoDataParser parser(GeoData_KML);
293 if (!parser.read(&file)) {
294 mDebug() << "Could not parse tracking file: " << parser.errorString();
295 return;
296 }
297
298 auto doc = dynamic_cast<GeoDataDocument *>(parser.releaseDocument());
299 file.close();
300
301 if (!doc) {
302 mDebug() << "tracking document not available";
303 return;
304 }
305
306 auto track = dynamic_cast<GeoDataPlacemark *>(doc->child(0));
307 if (!track) {
308 mDebug() << "tracking document doesn't have a placemark";
309 delete doc;
310 return;
311 }
312
313 d->m_trackSegments = dynamic_cast<GeoDataMultiTrack *>(track->geometry());
314 if (!d->m_trackSegments) {
315 mDebug() << "tracking document doesn't have a multitrack";
316 delete doc;
317 return;
318 }
319 if (d->m_trackSegments->size() < 1) {
320 mDebug() << "tracking document doesn't have a track";
321 delete doc;
322 return;
323 }
324
325 d->m_currentTrack = dynamic_cast<GeoDataTrack *>(d->m_trackSegments->child(d->m_trackSegments->size() - 1));
326 if (!d->m_currentTrack) {
327 mDebug() << "tracking document doesn't have a last track";
328 delete doc;
329 return;
330 }
331
332 doc->remove(0);
333 delete doc;
334
335 d->m_treeModel->removeDocument(&d->m_document);
336 d->m_document.remove(1);
337 delete d->m_currentTrackPlacemark;
338 d->m_currentTrackPlacemark = track;
339 d->m_currentTrackPlacemark->setName(QStringLiteral("Current Track"));
340 d->m_document.append(d->m_currentTrackPlacemark);
341 d->m_currentTrackPlacemark->setStyleUrl(d->m_currentTrackPlacemark->styleUrl());
342
343 d->m_treeModel->addDocument(&d->m_document);
344 d->m_length = 0.0;
345 for (int i = 0; i < d->m_trackSegments->size(); ++i) {
346 d->m_length += d->m_trackSegments->at(i).lineString()->length(1);
347 }
348}
349
350void PositionTracking::writeSettings()
351{
352 saveTrack(d->statusFile());
353}
354
355bool PositionTracking::isTrackEmpty() const
356{
357 if (d->m_trackSegments->size() < 1) {
358 return true;
359 }
360
361 if (d->m_trackSegments->size() == 1) {
362 return (d->m_currentTrack->size() == 0);
363 }
364
365 return false;
366}
367
368qreal PositionTracking::length(qreal planetRadius) const
369{
370 return d->m_length * planetRadius;
371}
372
373GeoDataAccuracy PositionTracking::accuracy() const
374{
375 return d->m_positionProvider ? d->m_positionProvider->accuracy() : GeoDataAccuracy();
376}
377
378GeoDataCoordinates PositionTracking::currentLocation() const
379{
380 return d->m_positionProvider ? d->m_positionProvider->position() : GeoDataCoordinates();
381}
382
383PositionProviderStatus PositionTracking::status() const
384{
385 return d->m_positionProvider ? d->m_positionProvider->status() : PositionProviderStatusUnavailable;
386}
387
388}
389
390#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()
QString name(GameStandardAction id)
KIOCORE_EXPORT QString dir(const QString &fileClass)
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 Fri Nov 8 2024 12:02:44 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.