1 // SPDX-License-Identifier: LGPL-2.1-or-later
2 //
3 // SPDX-FileCopyrightText: 2008 Henry de Valence <hdevalence@gmail.com>
4 // SPDX-FileCopyrightText: 2010 Dennis Nienhüser <nienhueser@kde.org>
5 // SPDX-FileCopyrightText: 2010-2013 Bernhard Beschow <bbeschow@cs.tu-berlin.de>
6 // SPDX-FileCopyrightText: 2011 Thibaut Gridel <tgridel@free.fr>
7 
8 #include "ReverseGeocodingRunnerManager.h"
9 
10 #include "MarbleDebug.h"
11 #include "MarbleModel.h"
12 #include "GeoDataCoordinates.h"
13 #include "GeoDataPlacemark.h"
14 #include "Planet.h"
15 #include "PluginManager.h"
16 #include "ReverseGeocodingRunnerPlugin.h"
17 #include "RunnerTask.h"
18 
19 #include <QList>
20 #include <QThreadPool>
21 #include <QTimer>
22 
23 namespace Marble
24 {
25 
26 class MarbleModel;
27 
28 class Q_DECL_HIDDEN ReverseGeocodingRunnerManager::Private
29 {
30 public:
31     Private( ReverseGeocodingRunnerManager *parent, const MarbleModel *marbleModel );
32 
33     QList<const ReverseGeocodingRunnerPlugin *> plugins( const QList<const ReverseGeocodingRunnerPlugin *> &plugins ) const;
34 
35     void addReverseGeocodingResult( const GeoDataCoordinates &coordinates, const GeoDataPlacemark &placemark );
36     void cleanupReverseGeocodingTask( ReverseGeocodingTask *task );
37 
38     ReverseGeocodingRunnerManager *const q;
39     const MarbleModel *const m_marbleModel;
40     const PluginManager* m_pluginManager;
41     QList<ReverseGeocodingTask*> m_reverseTasks;
42     QVector<GeoDataCoordinates> m_reverseGeocodingResults;
43     QString m_reverseGeocodingResult;
44 };
45 
Private(ReverseGeocodingRunnerManager * parent,const MarbleModel * marbleModel)46 ReverseGeocodingRunnerManager::Private::Private( ReverseGeocodingRunnerManager *parent, const MarbleModel *marbleModel ) :
47     q( parent ),
48     m_marbleModel( marbleModel ),
49     m_pluginManager( marbleModel->pluginManager() )
50 {
51     qRegisterMetaType<GeoDataPlacemark>( "GeoDataPlacemark" );
52     qRegisterMetaType<GeoDataCoordinates>( "GeoDataCoordinates" );
53 }
54 
plugins(const QList<const ReverseGeocodingRunnerPlugin * > & plugins) const55 QList<const ReverseGeocodingRunnerPlugin *> ReverseGeocodingRunnerManager::Private::plugins( const QList<const ReverseGeocodingRunnerPlugin *> &plugins ) const
56 {
57     QList<const ReverseGeocodingRunnerPlugin *> result;
58 
59     for( const ReverseGeocodingRunnerPlugin *plugin: plugins ) {
60         if ( ( m_marbleModel && m_marbleModel->workOffline() && !plugin->canWorkOffline() ) ) {
61             continue;
62         }
63 
64         if ( !plugin->canWork() ) {
65             continue;
66         }
67 
68         if ( m_marbleModel && !plugin->supportsCelestialBody( m_marbleModel->planet()->id() ) )
69         {
70             continue;
71         }
72 
73         result << plugin;
74     }
75 
76     return result;
77 }
78 
addReverseGeocodingResult(const GeoDataCoordinates & coordinates,const GeoDataPlacemark & placemark)79 void ReverseGeocodingRunnerManager::Private::addReverseGeocodingResult( const GeoDataCoordinates &coordinates, const GeoDataPlacemark &placemark )
80 {
81     if ( !m_reverseGeocodingResults.contains( coordinates ) && !placemark.address().isEmpty() ) {
82         m_reverseGeocodingResults.push_back( coordinates );
83         m_reverseGeocodingResult = placemark.address();
84         emit q->reverseGeocodingFinished( coordinates, placemark );
85     }
86 
87     if ( m_reverseTasks.isEmpty() ) {
88         emit q->reverseGeocodingFinished();
89     }
90 }
91 
cleanupReverseGeocodingTask(ReverseGeocodingTask * task)92 void ReverseGeocodingRunnerManager::Private::cleanupReverseGeocodingTask( ReverseGeocodingTask *task )
93 {
94     m_reverseTasks.removeAll( task );
95     mDebug() << "removing task " << m_reverseTasks.size() << " " << (quintptr)task;
96     if ( m_reverseTasks.isEmpty() ) {
97         emit q->reverseGeocodingFinished();
98     }
99 }
100 
ReverseGeocodingRunnerManager(const MarbleModel * marbleModel,QObject * parent)101 ReverseGeocodingRunnerManager::ReverseGeocodingRunnerManager( const MarbleModel *marbleModel, QObject *parent ) :
102     QObject( parent ),
103     d( new Private( this, marbleModel ) )
104 {
105     if ( QThreadPool::globalInstance()->maxThreadCount() < 4 ) {
106         QThreadPool::globalInstance()->setMaxThreadCount( 4 );
107     }
108 }
109 
~ReverseGeocodingRunnerManager()110 ReverseGeocodingRunnerManager::~ReverseGeocodingRunnerManager()
111 {
112     delete d;
113 }
114 
reverseGeocoding(const GeoDataCoordinates & coordinates)115 void ReverseGeocodingRunnerManager::reverseGeocoding( const GeoDataCoordinates &coordinates )
116 {
117     d->m_reverseTasks.clear();
118     d->m_reverseGeocodingResult.clear();
119     d->m_reverseGeocodingResults.removeAll( coordinates );
120 
121     QList<const ReverseGeocodingRunnerPlugin*> plugins = d->plugins( d->m_pluginManager->reverseGeocodingRunnerPlugins() );
122     for( const ReverseGeocodingRunnerPlugin* plugin: plugins ) {
123         ReverseGeocodingTask* task = new ReverseGeocodingTask( plugin->newRunner(), this, d->m_marbleModel, coordinates );
124         connect( task, SIGNAL(finished(ReverseGeocodingTask*)), this, SLOT(cleanupReverseGeocodingTask(ReverseGeocodingTask*)) );
125         mDebug() << "reverse task " << plugin->nameId() << " " << (quintptr)task;
126         d->m_reverseTasks << task;
127     }
128 
129     for( ReverseGeocodingTask* task: d->m_reverseTasks ) {
130         QThreadPool::globalInstance()->start( task );
131     }
132 
133     if ( plugins.isEmpty() ) {
134         GeoDataPlacemark anonymous;
135         anonymous.setCoordinate( coordinates );
136         emit reverseGeocodingFinished( coordinates, anonymous );
137         d->cleanupReverseGeocodingTask( nullptr );
138     }
139 }
140 
searchReverseGeocoding(const GeoDataCoordinates & coordinates,int timeout)141 QString ReverseGeocodingRunnerManager::searchReverseGeocoding( const GeoDataCoordinates &coordinates, int timeout ) {
142     QEventLoop localEventLoop;
143     QTimer watchdog;
144     watchdog.setSingleShot(true);
145     connect( &watchdog, SIGNAL(timeout()),
146              &localEventLoop, SLOT(quit()));
147     connect(this, SIGNAL(reverseGeocodingFinished()),
148             &localEventLoop, SLOT(quit()), Qt::QueuedConnection );
149 
150     watchdog.start( timeout );
151     reverseGeocoding( coordinates );
152     localEventLoop.exec();
153     return d->m_reverseGeocodingResult;
154 }
155 
156 }
157 
158 #include "moc_ReverseGeocodingRunnerManager.cpp"
159