Initial commit (software version 0.2.0)
[movie-schedule] / src / searchclients / gpsclient.cpp
1 // Copyright 2010 Jochen Becher
2 //
3 // This file is part of MovieSchedule.
4 //
5 // MovieSchedule is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // MovieSchedule is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with MovieSchedule.  If not, see <http://www.gnu.org/licenses/>.
17
18 #include "gpsclient.h"
19
20 #ifdef QT_MOBILITY_LOCATION
21 #include <QGeoPositionInfoSource>
22 #endif
23
24 #ifdef LIBLOCATION
25 #include <QTimer>
26 #endif
27
28 #include <QUrl>
29 #include <QNetworkAccessManager>
30 #include <QNetworkRequest>
31 #include <QNetworkReply>
32 #include <QMutexLocker>
33 #include <iostream>
34
35 #ifdef LIBLOCATION
36 static void changed(LocationGPSDevice *device, gpointer userdata)
37 {
38     Q_UNUSED(device);
39     ((GpsClient *) userdata)->GpsChanged();
40 }
41
42 static void connected(LocationGPSDevice *device, gpointer userdata)
43 {
44     Q_UNUSED(device);
45     ((GpsClient *) userdata)->GpsConnected();
46 }
47
48 static void disconnected(LocationGPSDevice *device, gpointer userdata)
49 {
50     Q_UNUSED(device);
51     ((GpsClient *) userdata)->GpsDisconnected();
52 }
53 #endif
54
55 GpsClient::GpsClient()
56     :
57 #ifdef QT_MOBILITY_LOCATION
58     _geo_position_info_source(QGeoPositionInfoSource::createDefaultSource(this)),
59 #endif
60 #ifdef LIBLOCATION
61     _location_gpsd_control(0),
62     _location_gps_device(0),
63     _time_out_timer(new QTimer(this)),
64 #endif
65     _network(new QNetworkAccessManager(this)),
66     _search_task_id(INVALID_SEARCH_TASK_ID)
67 {
68     {
69         QMutexLocker locker(&_next_search_task_id_mutex);
70         _search_task_id = _next_search_task_id++;
71     }
72 #ifdef QT_MOBILITY_LOCATION
73     connect(_geo_position_info_source, SIGNAL(positionUpdated(QGeoPositionInfo)), this, SLOT(GeoPositionUpdated(const QGeoPositionInfo &)));
74     connect(_geo_position_info_source, SIGNAL(updateTimeout()), this, SLOT(GeoPositionTimedOut()));
75 #endif
76 #ifdef LIBLOCATION
77     connect(_time_out_timer, SIGNAL(timeout()), this, SLOT(GpsTimedOut()));
78 #endif
79     connect(_network, SIGNAL(finished(QNetworkReply*)), this, SLOT(ReplyFinished(QNetworkReply*)));
80 }
81
82 GpsClient::~GpsClient()
83 {
84 #ifdef LIBLOCATION
85     if (_location_gps_device != 0)
86         g_object_unref(_location_gps_device);
87     if (_location_gpsd_control != 0)
88         location_gpsd_control_stop(_location_gpsd_control);
89 #endif
90 }
91
92 void GpsClient::SearchLocation()
93 {
94     _semaphore.Activate(GetSearchTaskId());
95 #ifdef QT_MOBILITY_LOCATION
96     _geo_position_info_source->requestUpdate(30 * 1000);
97     emit SearchStarted(GetSearchTaskId());
98 #elif defined(LIBLOCATION)
99     _location_gpsd_control = location_gpsd_control_get_default();
100     if (_location_gpsd_control != 0) {
101         location_gpsd_control_start(_location_gpsd_control);
102     }
103     emit SearchStarted(GetSearchTaskId());
104     _location_gps_device = (LocationGPSDevice *) g_object_new(LOCATION_TYPE_GPS_DEVICE, NULL);
105     if (_location_gps_device != 0) {
106         location_gps_device_reset_last_known(_location_gps_device);
107         g_signal_connect(_location_gps_device, "changed", G_CALLBACK(changed), this);
108         g_signal_connect(_location_gps_device, "connected", G_CALLBACK(connected), this);
109         g_signal_connect(_location_gps_device, "disconnected", G_CALLBACK(disconnected), this);
110     }
111     _time_out_timer->start(30 * 1000);
112 #else
113     emit SearchStarted(GetSearchTaskId());
114     emit SearchError(GetSearchTaskId());
115     emit SearchFinished(GetSearchTaskId(), false);
116     deleteLater();
117 #endif
118 }
119
120 void GpsClient::CancelAllRunningSearchs()
121 {
122     _semaphore.CancelAll();
123 }
124
125 #ifdef QT_MOBILITY_LOCATION
126 void GpsClient::GeoPositionUpdated(const QGeoPositionInfo &geo_position_info)
127 {
128     if (geo_position_info.coordinate().isValid()) {
129         //std::cout << "longitude " << geo_position_info.coordinate().longitude()
130         //        << ", latitude " << geo_position_info.coordinate().latitude()
131         //        << ", altitude " << geo_position_info.coordinate().altitude()
132         //        << std::endl;
133         SearchTown(QString("%1").arg(geo_position_info.coordinate().longitude()),
134                    QString("%1").arg(geo_position_info.coordinate().latitude()));
135         emit SearchForTownStarted(GetSearchTaskId());
136     } else {
137         //std::cout << "invalid coordinate received" << std::endl;
138         emit SearchError(GetSearchTaskId());
139         emit SearchFinished(GetSearchTaskId(), false);
140         deleteLater();
141     }
142 }
143
144 void GpsClient::GeoPositionTimedOut()
145 {
146     //std::cout << "time-out" << std::endl;
147     emit SearchError(GetSearchTaskId());
148     emit SearchFinished(GetSearchTaskId(), false);
149     deleteLater();
150 }
151 #endif
152
153 #ifdef LIBLOCATION
154 void GpsClient::GpsConnected()
155 {
156     //std::cout << "connected" << std::endl;
157 }
158
159 void GpsClient::GpsChanged()
160 {
161     if (_location_gps_device->status == LOCATION_GPS_DEVICE_STATUS_FIX
162         && _location_gps_device->fix != 0
163         && (_location_gps_device->fix->fields & LOCATION_GPS_DEVICE_LATLONG_SET) != 0)
164     {
165         //std::cout << "longitude " << _location_gps_device->fix->longitude
166         //        << ", latitude " << _location_gps_device->fix->latitude
167         //        << ", altitude " << _location_gps_device->fix->altitude
168         //        << ", eph " << _location_gps_device->fix->eph
169         //        << std::endl;
170         if (_location_gps_device->fix->eph != LOCATION_GPS_DEVICE_NAN
171             && _location_gps_device->fix->eph <= (20 * 1000 * 100)) // 20km
172         {
173             SearchTown(QString("%1").arg(_location_gps_device->fix->longitude),
174                        QString("%1").arg(_location_gps_device->fix->latitude));
175             emit SearchForTownStarted(GetSearchTaskId());
176         } else {
177             //std::cout << "waiting for better accuracy" << std::endl;
178         }
179     } else {
180         //std::cout << "waiting for location" << std::endl;
181     }
182 }
183
184 void GpsClient::GpsDisconnected()
185 {
186     //std::cout << "disconnected" << std::endl;
187 }
188
189 void GpsClient::GpsTimedOut()
190 {
191     //std::cout << "time-out" << std::endl;
192     emit SearchError(GetSearchTaskId());
193     emit SearchFinished(GetSearchTaskId(), false);
194     deleteLater();
195 }
196 #endif
197
198 void GpsClient::SearchTown(const QString &longitude, const QString &latitude)
199 {
200     // TODO: try to fetch a unique city name, at least with country code.
201     // http://code.google.com/intl/en/apis/maps/documentation/geocoding/index.html#ReverseGeocoding
202     QUrl url("http://maps.google.com/maps/geo");
203     url.addEncodedQueryItem("q", QUrl::toPercentEncoding(latitude + "," + longitude));
204     url.addEncodedQueryItem("output", QUrl::toPercentEncoding("xml"));
205     _network->get(QNetworkRequest(url));
206 }
207
208 void GpsClient::ReplyFinished(QNetworkReply *network_reply)
209 {
210     if (!network_reply->error()) {
211         QString data = QString::fromUtf8(network_reply->readAll());
212         int start = data.indexOf("<LocalityName>");
213         if (start >= 0) {
214             int end = data.indexOf("</LocalityName>", start);
215             QString town = data.mid(start + 14, end - start - 14);
216             if (!town.isEmpty()) {
217                 //std::cout << "Found town " << qPrintable(town) << std::endl;
218                 emit TownUpdate(GetSearchTaskId(), town);
219                 emit SearchFinished(GetSearchTaskId(), true);
220                 deleteLater();
221             } else {
222                 //std::cout << "No town found in " << qPrintable(data) << std::endl;
223                 emit SearchError(GetSearchTaskId());
224                 emit SearchFinished(GetSearchTaskId(), false);
225                 deleteLater();
226             }
227         } else {
228             //std::cout << "No <LocalityName> found in " << qPrintable(data) << std::endl;
229             emit SearchError(GetSearchTaskId());
230             emit SearchFinished(GetSearchTaskId(), false);
231             deleteLater();
232         }
233     }
234     network_reply->deleteLater();
235 }
236
237 void GpsClient::NetworkError(QNetworkReply::NetworkError)
238 {
239     //std::cout << "Network error" << std::endl;
240     emit SearchError(GetSearchTaskId());
241     emit SearchFinished(GetSearchTaskId(), false);
242     deleteLater();
243 }
244
245 QMutex GpsClient::_next_search_task_id_mutex;
246 int GpsClient::_next_search_task_id = 1;
247 SearchClientSemaphore GpsClient::_semaphore;