File indexing completed on 2024-12-08 06:35:35

0001 // SPDX-License-Identifier: LGPL-2.1-or-later
0002 //
0003 // SPDX-FileCopyrightText: 2011-2012 Florian Eßer <f.esser@rwth-aachen.de>
0004 // SPDX-FileCopyrightText: 2012 Bernhard Beschow <bbeschow@cs.tu-berlin.de>
0005 // SPDX-FileCopyrightText: 2013 Roman Karlstetter <roman.karlstetter@googlemail.com>
0006 //
0007 #include "ElevationProfileDataSource.h"
0008 
0009 #include "ElevationModel.h"
0010 
0011 #include "GeoDataDocument.h"
0012 #include "GeoDataLineString.h"
0013 #include "GeoDataObject.h"
0014 #include "GeoDataMultiGeometry.h"
0015 #include "GeoDataPlacemark.h"
0016 #include "GeoDataTrack.h"
0017 #include "GeoDataTreeModel.h"
0018 #include "MarbleDebug.h"
0019 #include "MarbleModel.h"
0020 #include "routing/Route.h"
0021 #include "routing/RoutingModel.h"
0022 
0023 #include <QFileInfo>
0024 
0025 namespace Marble
0026 {
0027 
0028 ElevationProfileDataSource::ElevationProfileDataSource( QObject *parent ) :
0029     QObject( parent )
0030 {
0031     // nothing to do
0032 }
0033 
0034 QVector<QPointF> ElevationProfileDataSource::calculateElevationData(const GeoDataLineString &lineString) const
0035 {
0036     // TODO: Don't re-calculate the whole route if only a small part of it was changed
0037     QVector<QPointF> result;
0038     qreal distance = 0;
0039 
0040     //GeoDataLineString path;
0041     for ( int i = 0; i < lineString.size(); i++ ) {
0042         const qreal ele = getElevation( lineString[i] );
0043 
0044         if ( i ) {
0045             distance += EARTH_RADIUS * lineString[i-1].sphericalDistanceTo(lineString[i]);
0046         }
0047 
0048         if ( ele != invalidElevationData ) { // skip no data
0049             result.append( QPointF( distance, ele ) );
0050         }
0051     }
0052 
0053     return result;
0054 }
0055 // end of impl of ElevationProfileDataSource
0056 
0057 ElevationProfileTrackDataSource::ElevationProfileTrackDataSource( const GeoDataTreeModel *treeModel, QObject *parent ) :
0058     ElevationProfileDataSource( parent ),
0059     m_currentSourceIndex( -1 )
0060 {
0061     if ( treeModel ) {
0062         connect( treeModel, SIGNAL(added(GeoDataObject*)), SLOT(handleObjectAdded(GeoDataObject*)) );
0063         connect( treeModel, SIGNAL(removed(GeoDataObject*)), SLOT(handleObjectRemoved(GeoDataObject*)) );
0064     }
0065 }
0066 
0067 QStringList ElevationProfileTrackDataSource::sourceDescriptions() const
0068 {
0069     return m_trackChooserList;
0070 }
0071 
0072 void ElevationProfileTrackDataSource::setSourceIndex(int index)
0073 {
0074     if (m_currentSourceIndex != index) {
0075         m_currentSourceIndex = index;
0076         requestUpdate();
0077     }
0078 }
0079 
0080 int ElevationProfileTrackDataSource::currentSourceIndex() const
0081 {
0082     return m_currentSourceIndex;
0083 }
0084 
0085 void ElevationProfileTrackDataSource::requestUpdate()
0086 {
0087     if ( m_currentSourceIndex < 0 ) {
0088         return;
0089     }
0090 
0091     if ( m_currentSourceIndex >= m_trackList.size() ) {
0092         return;
0093     }
0094 
0095     const GeoDataLineString *routePoints = m_trackList[m_currentSourceIndex]->lineString();
0096 
0097     emit dataUpdated(*routePoints, calculateElevationData(*routePoints));
0098 }
0099 
0100 bool ElevationProfileTrackDataSource::isDataAvailable() const
0101 {
0102     return !m_trackHash.isEmpty();
0103 }
0104 
0105 qreal ElevationProfileTrackDataSource::getElevation(const GeoDataCoordinates &coordinates) const
0106 {
0107     return coordinates.altitude();
0108 }
0109 
0110 void ElevationProfileTrackDataSource::handleObjectAdded(GeoDataObject *object)
0111 {
0112     const GeoDataDocument *document = dynamic_cast<const GeoDataDocument *>(object);
0113     if (!document) {
0114         return;// don't know what to do if not a document
0115     }
0116     QList<const GeoDataTrack *> trackList;
0117 
0118     for (int i = 0; i<document->size(); ++i) {
0119         const GeoDataFeature *feature = document->child(i);
0120         const GeoDataPlacemark *placemark = dynamic_cast<const GeoDataPlacemark*>(feature);
0121         if (!placemark) {
0122             continue;
0123         }
0124         const GeoDataMultiGeometry *multiGeometry = dynamic_cast<const GeoDataMultiGeometry *>(placemark->geometry());
0125         if (!multiGeometry) {
0126             continue;
0127         }
0128         for (int i = 0; i<multiGeometry->size(); i++) {
0129             const GeoDataTrack *track = dynamic_cast<const GeoDataTrack *>(multiGeometry->child(i));
0130             if (track && track->size() > 1) {
0131                 mDebug() << "new GeoDataTrack for ElevationProfile detected";
0132                 trackList.append(track);
0133             }
0134         }
0135     }
0136 
0137     if (trackList.isEmpty()) {
0138         return;
0139     }
0140 
0141     // update internal datastructures
0142     m_trackHash.insert(document->fileName(), trackList);
0143 
0144     const GeoDataTrack *selectedTrack = nullptr;
0145     if ( 0 <= m_currentSourceIndex && m_currentSourceIndex < m_trackList.size() ) {
0146         selectedTrack = m_trackList[m_currentSourceIndex];
0147     }
0148 
0149     m_trackChooserList.clear();
0150     m_trackList.clear();
0151     QHashIterator<QString, QList<const GeoDataTrack *> > i(m_trackHash);
0152     while (i.hasNext()) {
0153         i.next();
0154         mDebug() << i.key() << ": " << i.value() << endl;
0155         QFileInfo info(i.key());
0156         QString filename = info.fileName();
0157         QList<const GeoDataTrack *> list = i.value();
0158         for (int i = 0; i<list.size(); ++i) {
0159             m_trackList << list[i];
0160             m_trackChooserList << QString(filename + QLatin1String(": ") + QString::number(i));
0161         }
0162     }
0163     if (selectedTrack) {
0164         m_currentSourceIndex = m_trackList.indexOf(selectedTrack);
0165     }
0166 
0167     emit sourceCountChanged();
0168 }
0169 
0170 void ElevationProfileTrackDataSource::handleObjectRemoved(GeoDataObject *object)
0171 {
0172     if (m_trackList.size() == 0) {
0173         // no track loaded, nothing to remove
0174         return;
0175     }
0176 
0177     const GeoDataDocument *topLevelDoc = dynamic_cast<const GeoDataDocument*>(object);
0178     if (!topLevelDoc) {
0179         return;// don't know what to do if not a document
0180     }
0181 
0182     const QString key = topLevelDoc->fileName();
0183     if ( !m_trackHash.contains( key ) ) {
0184         return;
0185     }
0186 
0187     const QList<const GeoDataTrack *> list = m_trackHash.value(key);
0188     const GeoDataTrack *const selectedTrack = m_currentSourceIndex == -1 ? 0 : m_trackList[m_currentSourceIndex];
0189     for (int i = 0; i<list.size(); i++) {
0190         int idx = m_trackList.indexOf(list[i]);
0191         m_trackList.removeAt(idx);
0192         m_trackChooserList.removeAt(idx);
0193     }
0194     m_trackHash.remove(key);
0195 
0196     m_currentSourceIndex = m_trackList.indexOf(selectedTrack);
0197     if (m_currentSourceIndex == -1) {
0198         m_currentSourceIndex = 0;
0199     }
0200 
0201     emit sourceCountChanged();
0202     requestUpdate();
0203 }
0204 
0205 // end of impl of ElevationProfileTrackDataSource
0206 
0207 ElevationProfileRouteDataSource::ElevationProfileRouteDataSource( const RoutingModel *routingModel, const ElevationModel *elevationModel, QObject *parent ) :
0208     ElevationProfileDataSource( parent ),
0209     m_routingModel( routingModel ),
0210     m_elevationModel( elevationModel ),
0211     m_routeAvailable( false )
0212 {
0213 }
0214 
0215 void ElevationProfileRouteDataSource::requestUpdate()
0216 {
0217     if (m_routeAvailable != isDataAvailable()) {
0218         // availability of route changed
0219         emit sourceCountChanged();
0220         m_routeAvailable = isDataAvailable();
0221     }
0222 
0223     const GeoDataLineString routePoints = m_routingModel->route().path();
0224     const QVector<QPointF> elevationData = calculateElevationData(routePoints);
0225     emit dataUpdated( routePoints, elevationData );
0226 }
0227 
0228 bool ElevationProfileRouteDataSource::isDataAvailable() const
0229 {
0230     return m_routingModel && m_routingModel->rowCount() > 0;
0231 }
0232 
0233 qreal ElevationProfileRouteDataSource::getElevation(const GeoDataCoordinates &coordinates) const
0234 {
0235     const qreal lat = coordinates.latitude ( GeoDataCoordinates::Degree );
0236     const qreal lon = coordinates.longitude( GeoDataCoordinates::Degree );
0237     qreal ele = m_elevationModel->height( lon, lat );
0238     return ele;
0239 }
0240 // end of impl of ElevationProfileRouteDataSource
0241 
0242 }
0243 
0244 #include "moc_ElevationProfileDataSource.cpp"
0245