File indexing completed on 2025-01-05 03:59:01

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