Tizen 2.1 base
[platform/core/location/location-module.git] / modules / geoclue-nominatim.c
1 /*
2  * location-geoclue-nominatim
3  *
4  * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. All rights reserved.
5  *
6  * Contact: Youngae Kang <youngae.kang@samsung.com>, Yunhan Kim <yhan.kim@samsung.com>,
7  *          Genie Kim <daejins.kim@samsung.com>, Minjune Kim <sena06.kim@samsung.com>
8  *
9  * Licensed under the Apache License, Version 2.0 (the "License");
10  * you may not use this file except in compliance with the License.
11  * You may obtain a copy of the License at
12  *
13  * http://www.apache.org/licenses/LICENSE-2.0
14  *
15  * Unless required by applicable law or agreed to in writing, software
16  * distributed under the License is distributed on an "AS IS" BASIS,
17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18  * See the License for the specific language governing permissions and
19  * limitations under the License.
20  */
21
22 #include <glib.h>
23
24 #include <location-module.h>
25
26 #include <geoclue/geoclue-geocode.h>
27 #include <geoclue/geoclue-reverse-geocode.h>
28 #include <geoclue/geoclue-provider.h>
29
30 #ifdef HAVE_CONFIG_H
31 #include "config.h"
32 #endif
33 #include "log.h"
34
35 typedef struct {
36         char* service_name;
37         GeoclueGeocode* geocoder;
38         GeoclueReverseGeocode* rev_geocoder;
39 } NominatimHandle;
40
41 typedef struct {
42         LocationPositionCB pos_cb;
43         LocationAddressCB addr_cb;
44         gpointer userdata;
45 } NominatimData;
46
47 static GHashTable*
48 get_geoclue_addr_from_location_addr (const LocationAddress *address)
49 {
50         if (!address) return NULL;
51
52         GHashTable *geoclue_addr = geoclue_address_details_new();
53         if (address->street) {
54                 if (address->building_number) {
55                         char *street_addr = g_strdup_printf ("%s %s", address->building_number, address->street);
56                         geoclue_address_details_insert (geoclue_addr, GEOCLUE_ADDRESS_KEY_STREET, street_addr);
57                         g_free (street_addr);
58                 } else geoclue_address_details_insert (geoclue_addr, GEOCLUE_ADDRESS_KEY_STREET, address->street);
59         }
60         if (address->district)     geoclue_address_details_insert (geoclue_addr, GEOCLUE_ADDRESS_KEY_AREA, address->district);
61         if (address->city)         geoclue_address_details_insert (geoclue_addr, GEOCLUE_ADDRESS_KEY_LOCALITY, address->city);
62         if (address->state)        geoclue_address_details_insert (geoclue_addr, GEOCLUE_ADDRESS_KEY_REGION, address->state);
63         if (address->country_code) geoclue_address_details_insert (geoclue_addr, GEOCLUE_ADDRESS_KEY_COUNTRY, address->country_code);
64         if (address->postal_code)  geoclue_address_details_insert (geoclue_addr, GEOCLUE_ADDRESS_KEY_POSTALCODE, address->postal_code);
65
66         return geoclue_addr;
67 }
68
69 static LocationAddress*
70 get_location_addr_from_geoclue_addr (GHashTable *geoclue_addr)
71 {
72         if (!geoclue_addr) return NULL;
73
74         LocationAddress *addr = NULL;
75
76         gchar *country, *region, *locality, *postalcode, *area, *street;
77
78         country = g_hash_table_lookup (geoclue_addr, GEOCLUE_ADDRESS_KEY_COUNTRY);
79         region = g_hash_table_lookup (geoclue_addr, GEOCLUE_ADDRESS_KEY_REGION);
80         locality = g_hash_table_lookup (geoclue_addr, GEOCLUE_ADDRESS_KEY_LOCALITY);
81         area = g_hash_table_lookup (geoclue_addr, GEOCLUE_ADDRESS_KEY_AREA);
82         postalcode = g_hash_table_lookup (geoclue_addr, GEOCLUE_ADDRESS_KEY_POSTALCODE);
83         street = g_hash_table_lookup (geoclue_addr, GEOCLUE_ADDRESS_KEY_STREET);
84
85         addr = location_address_new (NULL, street, area, locality, region, country, postalcode);
86         return addr;
87 }
88
89 static LocationAccuracy *
90 get_location_acc_from_geoclue_acc (GeoclueAccuracy *acc)
91 {
92         if (!acc) return NULL;
93
94         LocationAccuracy *accuracy = NULL;
95         GeoclueAccuracyLevel level;
96         double horiz_acc;
97         double vert_acc;
98         geoclue_accuracy_get_details (acc, &level, &horiz_acc, &vert_acc);
99
100         if (level == GEOCLUE_ACCURACY_LEVEL_DETAILED)        accuracy = location_accuracy_new (LOCATION_ACCURACY_LEVEL_DETAILED, horiz_acc, vert_acc);
101         else if (level == GEOCLUE_ACCURACY_LEVEL_STREET)     accuracy = location_accuracy_new (LOCATION_ACCURACY_LEVEL_STREET, horiz_acc, vert_acc);
102         else if (level == GEOCLUE_ACCURACY_LEVEL_POSTALCODE) accuracy = location_accuracy_new (LOCATION_ACCURACY_LEVEL_POSTALCODE, horiz_acc, vert_acc);
103         else if (level == GEOCLUE_ACCURACY_LEVEL_LOCALITY)   accuracy = location_accuracy_new (LOCATION_ACCURACY_LEVEL_LOCALITY, horiz_acc, vert_acc);
104         else if (level == GEOCLUE_ACCURACY_LEVEL_REGION)     accuracy = location_accuracy_new (LOCATION_ACCURACY_LEVEL_REGION, horiz_acc, vert_acc);
105         else if (level == GEOCLUE_ACCURACY_LEVEL_COUNTRY)    accuracy = location_accuracy_new (LOCATION_ACCURACY_LEVEL_COUNTRY, horiz_acc, vert_acc);
106         else accuracy = location_accuracy_new (LOCATION_ACCURACY_LEVEL_NONE, 0, 0);
107
108         return accuracy;
109 }
110
111 static void
112 _unref_geoclue(NominatimHandle* handle)
113 {
114         if (handle->geocoder) {
115                 g_object_unref (handle->geocoder);
116                 handle->geocoder = NULL;
117         }
118         if (handle->rev_geocoder) {
119                 g_object_unref (handle->rev_geocoder);
120                 handle->rev_geocoder = NULL;
121         }
122 }
123
124 static gboolean
125 _ref_geoclue(NominatimHandle* handle)
126 {
127         gchar *service, *path;
128         service = g_strdup_printf ("org.freedesktop.Geoclue.Providers.Nominatim");
129         path = g_strdup_printf ("/org/freedesktop/Geoclue/Providers/Nominatim");
130
131         if (!handle->geocoder) handle->geocoder = geoclue_geocode_new (service, path);
132         if (!handle->rev_geocoder) handle->rev_geocoder = geoclue_reverse_geocode_new (service, path);
133
134         if(!handle->geocoder || !handle->rev_geocoder){
135                 MOD_LOGW ("Error while creating Geoclue object.");
136                 _unref_geoclue(handle);
137                 return FALSE;
138         }
139         return TRUE;
140 }
141
142 static void
143 address_cb (GeoclueReverseGeocode *revgeocode,
144         GHashTable            *details,
145         GeoclueAccuracy       *accuracy,
146         GError                *error,
147         gpointer               userdata)
148 {
149         NominatimData* data = (NominatimData*)userdata;
150         g_return_if_fail(data->addr_cb);
151
152         LocationAddress *addr = NULL;
153         LocationAccuracy *acc = NULL;
154         LocationError err = LOCATION_ERROR_NONE;
155
156         if (error) {
157                 MOD_LOGW ("Error getting reverse geocode: %s", error->message);
158                 err = LOCATION_ERROR_NOT_AVAILABLE;
159         } else {
160                 if (accuracy) acc = get_location_acc_from_geoclue_acc (accuracy);
161                 else acc = location_accuracy_new (LOCATION_ACCURACY_LEVEL_NONE, 0, 0);
162
163                 if (details) addr = get_location_addr_from_geoclue_addr (details);
164         }
165
166         data->addr_cb (err, addr, acc, data->userdata);
167         if (addr) location_address_free (addr);
168         if (acc) location_accuracy_free (acc);
169         g_free (data);
170 }
171
172 static void _free_pos_list_item(gpointer data)
173 {
174         g_return_if_fail(data);
175
176         LocationPosition *position = (LocationPosition *)data;
177         location_position_free(position);
178 }
179
180 static void _free_acc_list_item(gpointer data)
181 {
182         g_return_if_fail(data);
183
184         LocationAccuracy *accuracy = (LocationAccuracy *)data;
185         location_accuracy_free(accuracy);
186 }
187
188 static void position_cb (GeoclueGeocode       *geocode,
189         GeocluePositionFields fields,
190         double                latitude,
191         double                longitude,
192         double                altitude,
193         GeoclueAccuracy      *accuracy,
194         GError               *error,
195         gpointer              userdata)
196 {
197         NominatimData* data = (NominatimData*)userdata;
198         GList *pos_list = NULL;
199         GList *acc_list = NULL;
200
201         g_return_if_fail(data->pos_cb);
202
203         LocationPosition *pos = NULL;
204         LocationAccuracy *acc = NULL;
205         LocationError err = LOCATION_ERROR_NONE;
206
207         if (error) {
208                 MOD_LOGW ("Error getting geocode: %s", error->message);
209                 err = LOCATION_ERROR_NOT_AVAILABLE;
210         } else {
211                 if (fields & GEOCLUE_POSITION_FIELDS_LATITUDE &&
212                         fields & GEOCLUE_POSITION_FIELDS_LONGITUDE) {
213                         if(fields & GEOCLUE_POSITION_FIELDS_ALTITUDE) pos = location_position_new (0, latitude, longitude, altitude, LOCATION_STATUS_3D_FIX);
214                         else pos = location_position_new (0, latitude, longitude, 0, LOCATION_STATUS_2D_FIX);
215
216                         pos_list = g_list_append(pos_list, pos);
217                 } else {
218                         err = LOCATION_ERROR_NOT_FOUND;
219                 }
220
221                 if (accuracy) {
222                         acc = get_location_acc_from_geoclue_acc (accuracy);
223                         acc_list = g_list_append(acc_list, acc);
224                 }
225         }
226         data->pos_cb (err, pos_list, acc_list, data->userdata);
227
228         if (pos_list) g_list_free_full(pos_list, _free_pos_list_item);
229         if (acc_list) g_list_free_full(pos_list, _free_acc_list_item);
230         g_free (data);
231
232 }
233
234
235 static int
236 get_geocode (gpointer handle,
237         const LocationAddress *addr,
238         GList **pos_list,
239         GList **acc_list)
240 {
241         NominatimHandle *nominatim = (NominatimHandle *)handle;
242         g_return_val_if_fail(nominatim, LOCATION_ERROR_NOT_AVAILABLE);
243         g_return_val_if_fail(addr, LOCATION_ERROR_PARAMETER);
244         g_return_val_if_fail(pos_list, LOCATION_ERROR_PARAMETER);
245         g_return_val_if_fail(acc_list, LOCATION_ERROR_PARAMETER);
246
247         double lat, lon, alt;
248         GeoclueAccuracy *geoclue_acc = NULL;
249         GError *error = NULL;
250         LocationPosition *pos = NULL;
251         LocationAccuracy *acc = NULL;
252         LocationError err = LOCATION_ERROR_NONE;
253
254         GHashTable* geoclue_addr = get_geoclue_addr_from_location_addr (addr);
255         GeocluePositionFields fields = geoclue_geocode_address_to_position (nominatim->geocoder, geoclue_addr,
256                                                               &lat, &lon, &alt,
257                                                               &geoclue_acc, &error);
258         g_hash_table_destroy (geoclue_addr);
259
260         if (error) {
261                 MOD_LOGW ("Error getting geocode: %s", error->message);
262                 g_error_free (error);
263                 return LOCATION_ERROR_NOT_AVAILABLE;
264         }
265
266         if (fields & GEOCLUE_POSITION_FIELDS_LATITUDE &&
267                 fields & GEOCLUE_POSITION_FIELDS_LONGITUDE) {
268                 if(fields & GEOCLUE_POSITION_FIELDS_ALTITUDE) pos = location_position_new (0, lat, lon, alt, LOCATION_STATUS_3D_FIX);
269                         else pos = location_position_new (0, lat, lon, 0, LOCATION_STATUS_2D_FIX);
270
271                 *pos_list = g_list_append(*pos_list, pos);
272         }
273         else {
274                 err = LOCATION_ERROR_NOT_FOUND;
275         }
276
277         if (geoclue_acc) {
278                 acc = get_location_acc_from_geoclue_acc (geoclue_acc);
279                 geoclue_accuracy_free (geoclue_acc);
280
281                 *acc_list = g_list_append(*acc_list, acc);
282         }
283
284         if (pos) location_position_free (pos);
285         if (acc) location_accuracy_free (acc);
286
287         return err;
288 }
289
290
291 static int
292 get_geocode_async (gpointer handle,
293         const LocationAddress * addr,
294         LocationPositionCB callback,
295         gpointer userdata)
296 {
297         NominatimHandle *nominatim = (NominatimHandle *)handle;
298         g_return_val_if_fail(nominatim, LOCATION_ERROR_NOT_AVAILABLE);
299         g_return_val_if_fail(addr, LOCATION_ERROR_PARAMETER);
300         g_return_val_if_fail(callback, LOCATION_ERROR_PARAMETER);
301
302         GHashTable* geoclue_addr = get_geoclue_addr_from_location_addr (addr);
303         NominatimData *data = g_new0 (NominatimData, 1);
304         data->pos_cb = callback;
305         data->userdata = userdata;
306         geoclue_geocode_address_to_position_async (nominatim->geocoder, geoclue_addr, position_cb, data);
307         g_hash_table_destroy (geoclue_addr);
308
309         return LOCATION_ERROR_NONE;
310 }
311
312 static int
313 get_geocode_freetext(gpointer handle,
314         const const gchar* addr,
315         GList **pos_list,
316         GList **acc_list)
317 {
318         NominatimHandle *nominatim = (NominatimHandle *)handle;
319         g_return_val_if_fail(nominatim, LOCATION_ERROR_NOT_AVAILABLE);
320         g_return_val_if_fail(addr, LOCATION_ERROR_PARAMETER);
321         g_return_val_if_fail(pos_list, LOCATION_ERROR_PARAMETER);
322         g_return_val_if_fail(acc_list, LOCATION_ERROR_PARAMETER);
323
324         double lat, lon, alt;
325         GeoclueAccuracy *geoclue_acc = NULL;
326         GError *error = NULL;
327         LocationPosition *pos = NULL;
328         LocationAccuracy *acc = NULL;
329         LocationError err = LOCATION_ERROR_NONE;
330
331         GeocluePositionFields fields = geoclue_geocode_freeform_address_to_position (nominatim->geocoder, addr,
332                                                               &lat, &lon, &alt,
333                                                               &geoclue_acc, &error);
334         if (error) {
335                 MOD_LOGW ("Error getting geocode: %s", error->message);
336                 g_error_free (error);
337                 return LOCATION_ERROR_NOT_AVAILABLE;
338         }
339
340         if (fields & GEOCLUE_POSITION_FIELDS_LATITUDE &&
341                 fields & GEOCLUE_POSITION_FIELDS_LONGITUDE) {
342                 if(fields & GEOCLUE_POSITION_FIELDS_ALTITUDE) pos = location_position_new (0, lat, lon, alt, LOCATION_STATUS_3D_FIX);
343                 else pos = location_position_new (0, lat, lon, 0, LOCATION_STATUS_2D_FIX);
344                 *pos_list = g_list_append(*pos_list, pos);
345         }
346         else {
347                 err = LOCATION_ERROR_NOT_FOUND;
348         }
349
350         if (geoclue_acc) {
351                 acc = get_location_acc_from_geoclue_acc (geoclue_acc);
352                 geoclue_accuracy_free (geoclue_acc);
353                 *acc_list = g_list_append(*acc_list, acc);
354         }
355
356         if (pos) location_position_free (pos);
357         if (acc) location_accuracy_free (acc);
358         return err;
359 }
360
361
362 static int
363 get_geocode_freetext_async (gpointer handle,
364         const gchar* addr,
365         LocationPositionCB callback,
366         gpointer userdata)
367 {
368         NominatimHandle *nominatim = (NominatimHandle *)handle;
369         g_return_val_if_fail(nominatim, LOCATION_ERROR_NOT_AVAILABLE);
370         g_return_val_if_fail(addr, LOCATION_ERROR_PARAMETER);
371         g_return_val_if_fail(callback, LOCATION_ERROR_PARAMETER);
372
373         NominatimData *data = g_new0 (NominatimData, 1);
374         data->pos_cb = callback;
375         data->userdata = userdata;
376         geoclue_geocode_freeform_address_to_position_async (nominatim->geocoder, addr, position_cb, data);
377
378         return LOCATION_ERROR_NONE;
379 }
380
381 static int
382 get_reverse_geocode(gpointer handle,
383         const LocationPosition *pos,
384         LocationAddress **addr,
385         LocationAccuracy **acc)
386 {
387         NominatimHandle *nominatim = (NominatimHandle *)handle;
388         g_return_val_if_fail(nominatim, LOCATION_ERROR_NOT_AVAILABLE);
389         g_return_val_if_fail(pos, LOCATION_ERROR_PARAMETER);
390         g_return_val_if_fail(addr, LOCATION_ERROR_PARAMETER);
391         g_return_val_if_fail(acc, LOCATION_ERROR_PARAMETER);
392
393         GeoclueAccuracy *addr_acc = NULL;
394         GError *error = NULL;
395         GHashTable* geoclue_addr = NULL;
396
397         double lat = pos->latitude;
398         double lon = pos->longitude;
399         GeoclueAccuracy *pos_acc = geoclue_accuracy_new (GEOCLUE_ACCURACY_LEVEL_DETAILED, 0.0, 0.0);
400
401         gboolean success = geoclue_reverse_geocode_position_to_address (nominatim->rev_geocoder, lat, lon,
402                                                                         pos_acc, &geoclue_addr,
403                                                                         &addr_acc, &error);
404         geoclue_accuracy_free (pos_acc);
405
406         if (!success || error) {
407                 MOD_LOGW ("Error getting reverse geocode: %s", error->message);
408                 g_error_free (error);
409                 return LOCATION_ERROR_NOT_AVAILABLE;
410         }
411
412         if (addr_acc) {
413                 *acc = get_location_acc_from_geoclue_acc (addr_acc);
414                 geoclue_accuracy_free (addr_acc);
415         } else *acc = location_accuracy_new (LOCATION_ACCURACY_LEVEL_NONE, 0, 0);
416         if (geoclue_addr) {
417                 *addr = get_location_addr_from_geoclue_addr (geoclue_addr);
418                 g_hash_table_destroy (geoclue_addr);
419         }
420
421         return LOCATION_ERROR_NONE;
422 }
423
424 static int
425 get_reverse_geocode_async (gpointer handle,
426         const LocationPosition *pos,
427         LocationAddressCB callback,
428         gpointer userdata)
429 {
430         NominatimHandle *nominatim = (NominatimHandle *)handle;
431         g_return_val_if_fail(nominatim, LOCATION_ERROR_NOT_AVAILABLE);
432         g_return_val_if_fail(pos, LOCATION_ERROR_PARAMETER);
433         g_return_val_if_fail(callback, LOCATION_ERROR_PARAMETER);
434
435         double lat = pos->latitude;
436         double lon = pos->longitude;
437         GeoclueAccuracy *pos_acc = geoclue_accuracy_new (GEOCLUE_ACCURACY_LEVEL_DETAILED, 0.0, 0.0);;
438         NominatimData *data = g_new0 (NominatimData, 1);
439         data->addr_cb = callback;
440         data->userdata = userdata;
441         geoclue_reverse_geocode_position_to_address_async (nominatim->rev_geocoder,
442                                                 lat, lon, pos_acc,
443                                                 address_cb, data);
444         geoclue_accuracy_free (pos_acc);
445
446         return LOCATION_ERROR_NONE;
447 }
448
449 static int
450 get_service_name (gpointer handle,
451         gchar **service_name)
452 {
453         NominatimHandle *nominatim = (NominatimHandle *)handle;
454         g_return_val_if_fail(nominatim, LOCATION_ERROR_NOT_AVAILABLE);
455         g_return_val_if_fail (service_name, LOCATION_ERROR_PARAMETER);
456         *service_name = g_strdup (nominatim->service_name);
457         return LOCATION_ERROR_NONE;
458 }
459
460 LOCATION_MODULE_API gpointer
461 init (LocModGeoOps* ops)
462 {
463         MOD_LOGD("init");
464         g_return_val_if_fail(ops, NULL);
465         NominatimHandle *handle = g_new0 (NominatimHandle, 1);
466         if (!_ref_geoclue(handle)) {
467                 g_free (handle);
468                 return NULL;
469         }
470         handle->service_name = g_strdup ("Nominatim");
471         ops->get_service_name = get_service_name;
472         ops->get_geocode = get_geocode;
473         ops->get_geocode_freetext = get_geocode_freetext;
474         ops->get_reverse_geocode = get_reverse_geocode;
475         ops->get_geocode_async = get_geocode_async;
476         ops->get_geocode_freetext_async = get_geocode_freetext_async;
477         ops->get_reverse_geocode_async = get_reverse_geocode_async;
478         return (gpointer)handle;
479 }
480
481 LOCATION_MODULE_API void
482 shutdown (gpointer handle)
483 {
484         MOD_LOGD("shutdown");
485         g_return_if_fail(handle);
486         NominatimHandle *nominatim = (NominatimHandle *)handle;
487         g_free (nominatim->service_name);
488         _unref_geoclue(nominatim);
489         g_free (nominatim);
490 }