File indexing completed on 2024-05-12 03:50:18

0001 // SPDX-License-Identifier: LGPL-2.1-or-later
0002 //
0003 // SPDX-FileCopyrightText: 2011 Guillaume Martres <smarter@ubuntu.com>
0004 //
0005 
0006 #include "GeoDataTrack.h"
0007 #include "GeoDataGeometry_p.h"
0008 
0009 #include "GeoDataLatLonAltBox.h"
0010 #include "GeoDataTypes.h"
0011 #include "MarbleDebug.h"
0012 
0013 #include "GeoDataLineString.h"
0014 #include "GeoDataExtendedData.h"
0015 
0016 #include <QMap>
0017 #include <QDateTime>
0018 
0019 namespace Marble {
0020 
0021 class GeoDataTrackPrivate : public GeoDataGeometryPrivate
0022 {
0023 public:
0024     GeoDataTrackPrivate()
0025         : m_lineStringNeedsUpdate( false ),
0026           m_interpolate( false )
0027     {
0028     }
0029 
0030     GeoDataGeometryPrivate *copy() const override { return new GeoDataTrackPrivate( *this ); }
0031 
0032     void equalizeWhenSize()
0033     {
0034         m_when.reserve(m_coordinates.size());
0035         while ( m_when.size() < m_coordinates.size() ) {
0036             //fill coordinates without time information with null QDateTime
0037             m_when.append( QDateTime() );
0038         }
0039     }
0040 
0041     mutable GeoDataLineString m_lineString;
0042     mutable bool m_lineStringNeedsUpdate;
0043 
0044     bool m_interpolate;
0045 
0046     QVector<QDateTime> m_when;
0047     QVector<GeoDataCoordinates> m_coordinates;
0048 
0049     GeoDataExtendedData m_extendedData;
0050 };
0051 
0052 GeoDataTrack::GeoDataTrack() :
0053     GeoDataGeometry( new GeoDataTrackPrivate() )
0054 {
0055 
0056 }
0057 
0058 GeoDataTrack::GeoDataTrack( const GeoDataTrack &other )
0059     : GeoDataGeometry( other )
0060 {
0061 
0062 }
0063 
0064 GeoDataTrack &GeoDataTrack::operator=( const GeoDataTrack &other )
0065 {
0066     GeoDataGeometry::operator=( other );
0067 
0068     return *this;
0069 }
0070 
0071 const char *GeoDataTrack::nodeType() const
0072 {
0073     return GeoDataTypes::GeoDataTrackType;
0074 }
0075 
0076 EnumGeometryId GeoDataTrack::geometryId() const
0077 {
0078     return GeoDataTrackId;
0079 }
0080 
0081 GeoDataGeometry *GeoDataTrack::copy() const
0082 {
0083     return new GeoDataTrack(*this);
0084 }
0085 
0086 
0087 bool GeoDataTrack::operator==( const GeoDataTrack& other ) const
0088 {
0089     Q_D(const GeoDataTrack);
0090     const GeoDataTrackPrivate * const otherD = other.d_func();
0091 
0092     return equals(other) &&
0093            d->m_when == otherD->m_when &&
0094            d->m_coordinates == otherD->m_coordinates &&
0095            d->m_extendedData == otherD->m_extendedData &&
0096            d->m_interpolate == otherD->m_interpolate;
0097 }
0098 
0099 bool GeoDataTrack::operator!=( const GeoDataTrack& other ) const
0100 {
0101     return !this->operator==( other );
0102 }
0103 
0104 int GeoDataTrack::size() const
0105 {
0106     Q_D(const GeoDataTrack);
0107     return d->m_coordinates.size();
0108 }
0109 
0110 bool GeoDataTrack::interpolate() const
0111 {
0112     Q_D(const GeoDataTrack);
0113     return d->m_interpolate;
0114 }
0115 
0116 void GeoDataTrack::setInterpolate(bool on)
0117 {
0118     detach();
0119 
0120     Q_D(GeoDataTrack);
0121     d->m_interpolate = on;
0122 }
0123 
0124 QDateTime GeoDataTrack::firstWhen() const
0125 {
0126     Q_D(const GeoDataTrack);
0127 
0128     if (d->m_when.isEmpty()) {
0129         return QDateTime();
0130     }
0131 
0132     return d->m_when.first();
0133 }
0134 
0135 QDateTime GeoDataTrack::lastWhen() const
0136 {
0137     Q_D(const GeoDataTrack);
0138 
0139     if (d->m_when.isEmpty()) {
0140         return QDateTime();
0141     }
0142 
0143     return d->m_when.last();
0144 }
0145 
0146 QVector<GeoDataCoordinates> GeoDataTrack::coordinatesList() const
0147 {
0148     Q_D(const GeoDataTrack);
0149     return d->m_coordinates;
0150 }
0151 
0152 QVector<QDateTime> GeoDataTrack::whenList() const
0153 {
0154     Q_D(const GeoDataTrack);
0155     return d->m_when;
0156 }
0157 
0158 GeoDataCoordinates GeoDataTrack::coordinatesAt( const QDateTime &when ) const
0159 {
0160     Q_D(const GeoDataTrack);
0161 
0162     if (d->m_when.isEmpty()) {
0163         return GeoDataCoordinates();
0164     }
0165 
0166     if (d->m_when.contains(when)) {
0167         //exact match found
0168         const int index = d->m_when.indexOf(when);
0169         if (index < d->m_coordinates.size()) {
0170             return d->m_coordinates.at(index);
0171         }
0172     }
0173 
0174     if ( !interpolate() ) {
0175         return GeoDataCoordinates();
0176     }
0177 
0178     typedef QMap<QDateTime, GeoDataCoordinates> PointMap;
0179     PointMap pointMap;
0180     for (int i = 0; i < qMin(d->m_when.size(), d->m_coordinates.size()); ++i) {
0181         if (d->m_when.at(i).isValid()) {
0182             pointMap[d->m_when.at(i)] = d->m_coordinates.at(i);
0183         }
0184     }
0185 
0186     QMap<QDateTime, GeoDataCoordinates>::const_iterator nextEntry = const_cast<const PointMap&>(pointMap).upperBound( when );
0187 
0188     // No tracked point happened before "when"
0189     if ( nextEntry == pointMap.constBegin() ) {
0190         mDebug() << "No tracked point before " << when;
0191         return GeoDataCoordinates();
0192     }
0193 
0194     if ( nextEntry == pointMap.constEnd() ) {
0195         mDebug() << "No track point after" << when;
0196         return GeoDataCoordinates();
0197     }
0198 
0199     QMap<QDateTime, GeoDataCoordinates>::const_iterator previousEntry = nextEntry - 1;
0200     GeoDataCoordinates previousCoord = previousEntry.value();
0201 
0202     QDateTime previousWhen = previousEntry.key();
0203     QDateTime nextWhen = nextEntry.key();
0204     GeoDataCoordinates nextCoord = nextEntry.value();
0205 
0206     int interval = previousWhen.msecsTo( nextWhen );
0207     int position = previousWhen.msecsTo( when );
0208     qreal t = (qreal)position / (qreal)interval;
0209 
0210     return previousCoord.interpolate(nextCoord, t);
0211 }
0212 
0213 GeoDataCoordinates GeoDataTrack::coordinatesAt( int index ) const
0214 {
0215     Q_D(const GeoDataTrack);
0216     return d->m_coordinates.at(index);
0217 }
0218 
0219 void GeoDataTrack::addPoint( const QDateTime &when, const GeoDataCoordinates &coord )
0220 {
0221     detach();
0222 
0223     Q_D(GeoDataTrack);
0224     d->equalizeWhenSize();
0225     d->m_lineStringNeedsUpdate = true;
0226     int i=0;
0227     while (i < d->m_when.size()) {
0228         if (d->m_when.at(i) > when) {
0229             break;
0230         }
0231         ++i;
0232     }
0233     d->m_when.insert(i, when );
0234     d->m_coordinates.insert(i, coord );
0235 }
0236 
0237 void GeoDataTrack::appendCoordinates( const GeoDataCoordinates &coord )
0238 {
0239     detach();
0240 
0241     Q_D(GeoDataTrack);
0242     d->equalizeWhenSize();
0243     d->m_lineStringNeedsUpdate = true;
0244     d->m_coordinates.append(coord);
0245 }
0246 
0247 void GeoDataTrack::appendAltitude( qreal altitude )
0248 {
0249     detach();
0250 
0251     Q_D(GeoDataTrack);
0252     d->m_lineStringNeedsUpdate = true;
0253     Q_ASSERT(!d->m_coordinates.isEmpty());
0254     if (d->m_coordinates.isEmpty()) {
0255         return;
0256     }
0257     GeoDataCoordinates coordinates = d->m_coordinates.takeLast();
0258     coordinates.setAltitude( altitude );
0259     d->m_coordinates.append(coordinates);
0260 }
0261 
0262 void GeoDataTrack::appendWhen( const QDateTime &when )
0263 {
0264     detach();
0265 
0266     Q_D(GeoDataTrack);
0267     d->m_when.append(when);
0268 }
0269 
0270 void GeoDataTrack::clear()
0271 {
0272     detach();
0273 
0274     Q_D(GeoDataTrack);
0275     d->m_when.clear();
0276     d->m_coordinates.clear();
0277     d->m_lineStringNeedsUpdate = true;
0278 }
0279 
0280 void GeoDataTrack::removeBefore( const QDateTime &when )
0281 {
0282     detach();
0283 
0284     Q_D(GeoDataTrack);
0285     Q_ASSERT( d->m_coordinates.size() == d->m_when.size());
0286     if (d->m_when.isEmpty()) {
0287         return;
0288     }
0289     d->equalizeWhenSize();
0290 
0291     while (!d->m_when.isEmpty() && d->m_when.first() < when) {
0292         d->m_when.takeFirst();
0293         d->m_coordinates.takeFirst();
0294     }
0295 }
0296 
0297 void GeoDataTrack::removeAfter( const QDateTime &when )
0298 {
0299     detach();
0300 
0301     Q_D(GeoDataTrack);
0302     Q_ASSERT(d->m_coordinates.size() == d->m_when.size());
0303     if (d->m_when.isEmpty()) {
0304         return;
0305     }
0306     d->equalizeWhenSize();
0307     while (!d->m_when.isEmpty() && d->m_when.last() > when) {
0308         d->m_when.takeLast();
0309         d->m_coordinates.takeLast();
0310     }
0311 }
0312 
0313 const GeoDataLineString *GeoDataTrack::lineString() const
0314 {
0315     Q_D(const GeoDataTrack);
0316     if (d->m_lineStringNeedsUpdate) {
0317         d->m_lineString = GeoDataLineString();
0318         d->m_lineString.append( coordinatesList() );
0319         d->m_lineStringNeedsUpdate = false;
0320     }
0321     return &d->m_lineString;
0322 }
0323 
0324 GeoDataExtendedData& GeoDataTrack::extendedData()
0325 {
0326     detach();
0327 
0328     Q_D(GeoDataTrack);
0329     return d->m_extendedData;
0330 }
0331 
0332 const GeoDataExtendedData& GeoDataTrack::extendedData() const
0333 {
0334     Q_D(const GeoDataTrack);
0335     return d->m_extendedData;
0336 }
0337 
0338 void GeoDataTrack::setExtendedData( const GeoDataExtendedData& extendedData )
0339 {
0340     detach();
0341 
0342     Q_D(GeoDataTrack);
0343     d->m_extendedData = extendedData;
0344 }
0345 
0346 const GeoDataLatLonAltBox& GeoDataTrack::latLonAltBox() const
0347 {
0348     return lineString()->latLonAltBox();
0349 }
0350 
0351 //TODO
0352 void GeoDataTrack::pack( QDataStream& stream ) const
0353 {
0354     GeoDataGeometry::pack( stream );
0355 }
0356 //TODO
0357 void GeoDataTrack::unpack( QDataStream& stream )
0358 {
0359     GeoDataGeometry::unpack( stream );
0360 }
0361 
0362 }