File indexing completed on 2024-04-21 03:49:55

0001 // SPDX-License-Identifier: LGPL-2.1-or-later
0002 //
0003 // SPDX-FileCopyrightText: 2008 Henry de Valence <hdevalence@gmail.com>
0004 // SPDX-FileCopyrightText: 2010 Dennis Nienhüser <nienhueser@kde.org>
0005 // SPDX-FileCopyrightText: 2010-2013 Bernhard Beschow <bbeschow@cs.tu-berlin.de>
0006 // SPDX-FileCopyrightText: 2011 Thibaut Gridel <tgridel@free.fr>
0007 
0008 #include "SearchRunnerManager.h"
0009 
0010 #include "MarblePlacemarkModel.h"
0011 #include "MarbleDebug.h"
0012 #include "MarbleModel.h"
0013 #include "Planet.h"
0014 #include "GeoDataPlacemark.h"
0015 #include "PluginManager.h"
0016 #include "ParseRunnerPlugin.h"
0017 #include "ReverseGeocodingRunnerPlugin.h"
0018 #include "RoutingRunnerPlugin.h"
0019 #include "SearchRunnerPlugin.h"
0020 #include "RunnerTask.h"
0021 #include "routing/RouteRequest.h"
0022 #include "routing/RoutingProfilesModel.h"
0023 
0024 #include <QString>
0025 #include <QThreadPool>
0026 #include <QTimer>
0027 #include <QMutex>
0028 
0029 namespace Marble
0030 {
0031 
0032 class MarbleModel;
0033 
0034 class Q_DECL_HIDDEN SearchRunnerManager::Private
0035 {
0036 public:
0037     Private( SearchRunnerManager *parent, const MarbleModel *marbleModel );
0038 
0039     template<typename T>
0040     QList<T*> plugins( const QList<T*> &plugins ) const;
0041 
0042     void addSearchResult( const QVector<GeoDataPlacemark *> &result );
0043     void cleanupSearchTask( SearchTask *task );
0044     void notifySearchResultChange();
0045     void notifySearchFinished();
0046 
0047     SearchRunnerManager *const q;
0048     const MarbleModel *const m_marbleModel;
0049     const PluginManager* m_pluginManager;
0050     QString m_lastSearchTerm;
0051     GeoDataLatLonBox m_lastPreferredBox;
0052     QMutex m_modelMutex;
0053     MarblePlacemarkModel m_model;
0054     QList<SearchTask *> m_searchTasks;
0055     QVector<GeoDataPlacemark *> m_placemarkContainer;
0056 };
0057 
0058 SearchRunnerManager::Private::Private( SearchRunnerManager *parent, const MarbleModel *marbleModel ) :
0059     q( parent ),
0060     m_marbleModel( marbleModel ),
0061     m_pluginManager( marbleModel->pluginManager() ),
0062     m_model( new MarblePlacemarkModel( parent ) )
0063 {
0064     m_model.setPlacemarkContainer( &m_placemarkContainer );
0065     qRegisterMetaType<QVector<GeoDataPlacemark *> >( "QVector<GeoDataPlacemark*>" );
0066 }
0067 
0068 template<typename T>
0069 QList<T*> SearchRunnerManager::Private::plugins( const QList<T*> &plugins ) const
0070 {
0071     QList<T*> result;
0072     for( T* plugin: plugins ) {
0073         if ( ( m_marbleModel && m_marbleModel->workOffline() && !plugin->canWorkOffline() ) ) {
0074             continue;
0075         }
0076 
0077         if ( !plugin->canWork() ) {
0078             continue;
0079         }
0080 
0081         if ( m_marbleModel && !plugin->supportsCelestialBody( m_marbleModel->planet()->id() ) )
0082         {
0083             continue;
0084         }
0085 
0086         result << plugin;
0087     }
0088 
0089     return result;
0090 }
0091 
0092 void SearchRunnerManager::Private::addSearchResult( const QVector<GeoDataPlacemark *> &result )
0093 {
0094     mDebug() << "Runner reports" << result.size() << " search results";
0095     if( result.isEmpty() )
0096         return;
0097 
0098     m_modelMutex.lock();
0099     int start = m_placemarkContainer.size();
0100     int count = 0;
0101     bool distanceCompare = m_marbleModel->planet() != nullptr;
0102     for( int i=0; i<result.size(); ++i ) {
0103         bool same = false;
0104         for ( int j=0; j<m_placemarkContainer.size(); ++j ) {
0105             if ( distanceCompare &&
0106                  (result[i]->coordinate().sphericalDistanceTo(m_placemarkContainer[j]->coordinate())
0107                    * m_marbleModel->planet()->radius() < 1 ) ) {
0108                 same = true;
0109             }
0110         }
0111         if ( !same ) {
0112             m_placemarkContainer.append( result[i] );
0113             ++count;
0114         }
0115     }
0116     m_model.addPlacemarks( start, count );
0117     m_modelMutex.unlock();
0118     notifySearchResultChange();
0119 }
0120 
0121 void SearchRunnerManager::Private::cleanupSearchTask( SearchTask *task )
0122 {
0123     m_searchTasks.removeAll( task );
0124     mDebug() << "removing search task" << m_searchTasks.size() << (quintptr)task;
0125     if ( m_searchTasks.isEmpty() ) {
0126         if( m_placemarkContainer.isEmpty() ) {
0127             notifySearchResultChange();
0128         }
0129         notifySearchFinished();
0130     }
0131 }
0132 
0133 void SearchRunnerManager::Private::notifySearchResultChange()
0134 {
0135     emit q->searchResultChanged(&m_model);
0136     emit q->searchResultChanged(m_placemarkContainer);
0137 }
0138 
0139 void SearchRunnerManager::Private::notifySearchFinished()
0140 {
0141     emit q->searchFinished(m_lastSearchTerm);
0142     emit q->placemarkSearchFinished();
0143 }
0144 
0145 SearchRunnerManager::SearchRunnerManager( const MarbleModel *marbleModel, QObject *parent ) :
0146     QObject( parent ),
0147     d( new Private( this, marbleModel ) )
0148 {
0149     if ( QThreadPool::globalInstance()->maxThreadCount() < 4 ) {
0150         QThreadPool::globalInstance()->setMaxThreadCount( 4 );
0151     }
0152 }
0153 
0154 SearchRunnerManager::~SearchRunnerManager()
0155 {
0156     delete d;
0157 }
0158 
0159 void SearchRunnerManager::findPlacemarks( const QString &searchTerm, const GeoDataLatLonBox &preferred )
0160 {
0161     if ( searchTerm == d->m_lastSearchTerm && preferred == d->m_lastPreferredBox ) {
0162       d->notifySearchResultChange();
0163       d->notifySearchFinished();
0164       return;
0165     }
0166 
0167     d->m_lastSearchTerm = searchTerm;
0168     d->m_lastPreferredBox = preferred;
0169 
0170     d->m_searchTasks.clear();
0171 
0172     d->m_modelMutex.lock();
0173     bool placemarkContainerChanged = false;
0174     if (!d->m_placemarkContainer.isEmpty()) {
0175         d->m_model.removePlacemarks( "PlacemarkRunnerManager", 0, d->m_placemarkContainer.size() );
0176         qDeleteAll( d->m_placemarkContainer );
0177         d->m_placemarkContainer.clear();
0178         placemarkContainerChanged = true;
0179     }
0180     d->m_modelMutex.unlock();
0181     if (placemarkContainerChanged) {
0182         d->notifySearchResultChange();
0183     }
0184 
0185     if ( searchTerm.trimmed().isEmpty() ) {
0186         d->notifySearchFinished();
0187         return;
0188     }
0189 
0190     QList<const SearchRunnerPlugin *> plugins = d->plugins( d->m_pluginManager->searchRunnerPlugins() );
0191     for( const SearchRunnerPlugin *plugin: plugins ) {
0192         SearchTask *task = new SearchTask( plugin->newRunner(), this, d->m_marbleModel, searchTerm, preferred );
0193         connect( task, SIGNAL(finished(SearchTask*)), this, SLOT(cleanupSearchTask(SearchTask*)) );
0194         d->m_searchTasks << task;
0195         mDebug() << "search task " << plugin->nameId() << " " << (quintptr)task;
0196     }
0197 
0198     for( SearchTask *task: d->m_searchTasks ) {
0199         QThreadPool::globalInstance()->start( task );
0200     }
0201 
0202     if ( plugins.isEmpty() ) {
0203         d->cleanupSearchTask( nullptr );
0204     }
0205 }
0206 
0207 QVector<GeoDataPlacemark *> SearchRunnerManager::searchPlacemarks( const QString &searchTerm, const GeoDataLatLonBox &preferred, int timeout )
0208 {
0209     QEventLoop localEventLoop;
0210     QTimer watchdog;
0211     watchdog.setSingleShot(true);
0212     connect( &watchdog, SIGNAL(timeout()),
0213              &localEventLoop, SLOT(quit()));
0214     connect(this, SIGNAL(placemarkSearchFinished()),
0215             &localEventLoop, SLOT(quit()), Qt::QueuedConnection );
0216 
0217     watchdog.start( timeout );
0218     findPlacemarks( searchTerm, preferred );
0219     localEventLoop.exec();
0220     return d->m_placemarkContainer;
0221 }
0222 
0223 }
0224 
0225 #include "moc_SearchRunnerManager.cpp"