Marble

GeoDataTrack.cpp
1// SPDX-License-Identifier: LGPL-2.1-or-later
2//
3// SPDX-FileCopyrightText: 2011 Guillaume Martres <smarter@ubuntu.com>
4//
5
6#include "GeoDataTrack.h"
7#include "GeoDataGeometry_p.h"
8
9#include "GeoDataLatLonAltBox.h"
10#include "GeoDataTypes.h"
11#include "MarbleDebug.h"
12
13#include "GeoDataExtendedData.h"
14#include "GeoDataLineString.h"
15
16#include <QDateTime>
17#include <QMap>
18
19namespace Marble
20{
21
22class GeoDataTrackPrivate : public GeoDataGeometryPrivate
23{
24public:
25 GeoDataTrackPrivate()
26 : m_lineStringNeedsUpdate(false)
27 , m_interpolate(false)
28 {
29 }
30
31 GeoDataGeometryPrivate *copy() const override
32 {
33 return new GeoDataTrackPrivate(*this);
34 }
35
36 void equalizeWhenSize()
37 {
38 m_when.reserve(m_coordinates.size());
39 while (m_when.size() < m_coordinates.size()) {
40 // fill coordinates without time information with null QDateTime
41 m_when.append(QDateTime());
42 }
43 }
44
45 mutable GeoDataLineString m_lineString;
46 mutable bool m_lineStringNeedsUpdate;
47
48 bool m_interpolate;
49
50 QList<QDateTime> m_when;
51 QList<GeoDataCoordinates> m_coordinates;
52
53 GeoDataExtendedData m_extendedData;
54};
55
56GeoDataTrack::GeoDataTrack()
57 : GeoDataGeometry(new GeoDataTrackPrivate())
58{
59}
60
61GeoDataTrack::GeoDataTrack(const GeoDataTrack &other)
62
63 = default;
64
65GeoDataTrack &GeoDataTrack::operator=(const GeoDataTrack &other) = default;
66
67const char *GeoDataTrack::nodeType() const
68{
69 return GeoDataTypes::GeoDataTrackType;
70}
71
72EnumGeometryId GeoDataTrack::geometryId() const
73{
74 return GeoDataTrackId;
75}
76
77GeoDataGeometry *GeoDataTrack::copy() const
78{
79 return new GeoDataTrack(*this);
80}
81
82bool GeoDataTrack::operator==(const GeoDataTrack &other) const
83{
84 Q_D(const GeoDataTrack);
85 const GeoDataTrackPrivate *const otherD = other.d_func();
86
87 return equals(other) && d->m_when == otherD->m_when && d->m_coordinates == otherD->m_coordinates && d->m_extendedData == otherD->m_extendedData
88 && d->m_interpolate == otherD->m_interpolate;
89}
90
91bool GeoDataTrack::operator!=(const GeoDataTrack &other) const
92{
93 return !this->operator==(other);
94}
95
97{
98 Q_D(const GeoDataTrack);
99 return d->m_coordinates.size();
100}
101
103{
104 Q_D(const GeoDataTrack);
105 return d->m_interpolate;
106}
107
109{
110 detach();
111
112 Q_D(GeoDataTrack);
113 d->m_interpolate = on;
114}
115
117{
118 Q_D(const GeoDataTrack);
119
120 if (d->m_when.isEmpty()) {
121 return {};
122 }
123
124 return d->m_when.first();
125}
126
128{
129 Q_D(const GeoDataTrack);
130
131 if (d->m_when.isEmpty()) {
132 return {};
133 }
134
135 return d->m_when.last();
136}
137
139{
140 Q_D(const GeoDataTrack);
141 return d->m_coordinates;
142}
143
145{
146 Q_D(const GeoDataTrack);
147 return d->m_when;
148}
149
151{
152 Q_D(const GeoDataTrack);
153
154 if (d->m_when.isEmpty()) {
155 return {};
156 }
157
158 if (d->m_when.contains(when)) {
159 // exact match found
160 const int index = d->m_when.indexOf(when);
161 if (index < d->m_coordinates.size()) {
162 return d->m_coordinates.at(index);
163 }
164 }
165
166 if (!interpolate()) {
167 return {};
168 }
169
171 PointMap pointMap;
172 for (int i = 0; i < qMin(d->m_when.size(), d->m_coordinates.size()); ++i) {
173 if (d->m_when.at(i).isValid()) {
174 pointMap[d->m_when.at(i)] = d->m_coordinates.at(i);
175 }
176 }
177
178 QMap<QDateTime, GeoDataCoordinates>::const_iterator nextEntry = const_cast<const PointMap &>(pointMap).upperBound(when);
179
180 // No tracked point happened before "when"
181 if (nextEntry == pointMap.constBegin()) {
182 mDebug() << "No tracked point before " << when;
183 return {};
184 }
185
186 if (nextEntry == pointMap.constEnd()) {
187 mDebug() << "No track point after" << when;
188 return {};
189 }
190
191 QMap<QDateTime, GeoDataCoordinates>::const_iterator previousEntry = nextEntry - 1;
192 GeoDataCoordinates previousCoord = previousEntry.value();
193
194 QDateTime previousWhen = previousEntry.key();
195 QDateTime nextWhen = nextEntry.key();
196 GeoDataCoordinates nextCoord = nextEntry.value();
197
198 int interval = previousWhen.msecsTo(nextWhen);
199 int position = previousWhen.msecsTo(when);
200 qreal t = (qreal)position / (qreal)interval;
201
202 return previousCoord.interpolate(nextCoord, t);
203}
204
206{
207 Q_D(const GeoDataTrack);
208 return d->m_coordinates.at(index);
209}
210
212{
213 detach();
214
215 Q_D(GeoDataTrack);
216 d->equalizeWhenSize();
217 d->m_lineStringNeedsUpdate = true;
218 int i = 0;
219 while (i < d->m_when.size()) {
220 if (d->m_when.at(i) > when) {
221 break;
222 }
223 ++i;
224 }
225 d->m_when.insert(i, when);
226 d->m_coordinates.insert(i, coord);
227}
228
230{
231 detach();
232
233 Q_D(GeoDataTrack);
234 d->equalizeWhenSize();
235 d->m_lineStringNeedsUpdate = true;
236 d->m_coordinates.append(coord);
237}
238
240{
241 detach();
242
243 Q_D(GeoDataTrack);
244 d->m_lineStringNeedsUpdate = true;
245 Q_ASSERT(!d->m_coordinates.isEmpty());
246 if (d->m_coordinates.isEmpty()) {
247 return;
248 }
249 GeoDataCoordinates coordinates = d->m_coordinates.takeLast();
250 coordinates.setAltitude(altitude);
251 d->m_coordinates.append(coordinates);
252}
253
255{
256 detach();
257
258 Q_D(GeoDataTrack);
259 d->m_when.append(when);
260}
261
263{
264 detach();
265
266 Q_D(GeoDataTrack);
267 d->m_when.clear();
268 d->m_coordinates.clear();
269 d->m_lineStringNeedsUpdate = true;
270}
271
273{
274 detach();
275
276 Q_D(GeoDataTrack);
277 Q_ASSERT(d->m_coordinates.size() == d->m_when.size());
278 if (d->m_when.isEmpty()) {
279 return;
280 }
281 d->equalizeWhenSize();
282
283 while (!d->m_when.isEmpty() && d->m_when.first() < when) {
284 d->m_when.takeFirst();
285 d->m_coordinates.takeFirst();
286 }
287}
288
290{
291 detach();
292
293 Q_D(GeoDataTrack);
294 Q_ASSERT(d->m_coordinates.size() == d->m_when.size());
295 if (d->m_when.isEmpty()) {
296 return;
297 }
298 d->equalizeWhenSize();
299 while (!d->m_when.isEmpty() && d->m_when.last() > when) {
300 d->m_when.takeLast();
301 d->m_coordinates.takeLast();
302 }
303}
304
306{
307 Q_D(const GeoDataTrack);
308 if (d->m_lineStringNeedsUpdate) {
309 d->m_lineString = GeoDataLineString();
310 d->m_lineString.append(coordinatesList());
311 d->m_lineStringNeedsUpdate = false;
312 }
313 return &d->m_lineString;
314}
315
317{
318 detach();
319
321 return d->m_extendedData;
322}
323
325{
326 Q_D(const GeoDataTrack);
327 return d->m_extendedData;
328}
329
331{
332 detach();
333
334 Q_D(GeoDataTrack);
335 d->m_extendedData = extendedData;
336}
337
338const GeoDataLatLonAltBox &GeoDataTrack::latLonAltBox() const
339{
340 return lineString()->latLonAltBox();
341}
342
343// TODO
344void GeoDataTrack::pack(QDataStream &stream) const
345{
346 GeoDataGeometry::pack(stream);
347}
348// TODO
349void GeoDataTrack::unpack(QDataStream &stream)
350{
351 GeoDataGeometry::unpack(stream);
352}
353
354}
A 3d point representation.
void setAltitude(const qreal altitude)
set the altitude of the Point in meters
GeoDataCoordinates interpolate(const GeoDataCoordinates &target, double t) const
slerp (spherical linear) interpolation between this coordinate and the given target coordinate
a class which allows to add custom data to KML Feature.
A base class for all geodata features.
A class that defines a 3D bounding box for geographic data.
A LineString that allows to store a contiguous set of line segments.
A geometry for tracking objects made of (time, coordinates) pairs.
void clear()
Remove all the points contained in the track.
int size() const
Returns the number of points in the track.
void appendAltitude(qreal altitude)
Add altitude information to the last appended coordinates.
void addPoint(const QDateTime &when, const GeoDataCoordinates &coord)
Add a new point with coordinates coord associated with the time value when.
QDateTime lastWhen() const
Return the time value of the last point in the track, or an invalid QDateTime if the track is empty.
void setInterpolate(bool on)
Set whether coordinatesAt() should use interpolation.
void appendCoordinates(const GeoDataCoordinates &coord)
Add the coordinates part for a new point.
void appendWhen(const QDateTime &when)
Add the time value part for a new point.
bool interpolate() const
Returns true if coordinatesAt() should use interpolation, false otherwise.
GeoDataCoordinates coordinatesAt(const QDateTime &when) const
If interpolate() is true, return the coordinates interpolated from the time values before and after w...
QList< QDateTime > whenList() const
Returns the time value of all the points in the map, in chronological order.
void removeAfter(const QDateTime &when)
Remove all points from the track whose time value is greater than when.
void setExtendedData(const GeoDataExtendedData &extendedData)
Sets the ExtendedData of the feature.
const GeoDataExtendedData & extendedData() const
Return the ExtendedData assigned to the feature.
QDateTime firstWhen() const
Return the time value of the first point in the track, or an invalid QDateTime if the track is empty.
void removeBefore(const QDateTime &when)
Remove all points from the track whose time value is less than when.
bool operator==(const GeoDataTrack &other) const
: Equality operators.
const char * nodeType() const override
Provides type information for downcasting a GeoNode.
QList< GeoDataCoordinates > coordinatesList() const
Returns the coordinates of all the points in the map, sorted by their time value.
const GeoDataLineString * lineString() const
Return the GeoDataLineString representing the current track.
Binds a QML item to a specific geodetic location in screen coordinates.
qint64 msecsTo(const QDateTime &other) const const
Q_D(Todo)
This file is part of the KDE documentation.
Documentation copyright © 1996-2025 The KDE developers.
Generated on Fri Jan 24 2025 11:52:09 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.