[geocode-glib] lib: Add forward geocoding search
- From: Bastien Nocera <hadess src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [geocode-glib] lib: Add forward geocoding search
- Date: Fri, 19 Oct 2012 17:15:58 +0000 (UTC)
commit c9b72cad8658e0ab23c941eebfc5d193c81a4836
Author: Bastien Nocera <hadess hadess net>
Date: Fri Oct 19 18:54:20 2012 +0200
lib: Add forward geocoding search
Through the Yahoo! GeoPlanet API.
geocode-glib/geocode-glib-private.h | 6 +-
geocode-glib/geocode-glib.c | 374 +++++++++++++++++++++++++++++++----
geocode-glib/geocode-glib.h | 12 ++
geocode-glib/geocode-glib.symbols | 6 +-
geocode-glib/test-gcglib.c | 2 +-
5 files changed, 362 insertions(+), 38 deletions(-)
---
diff --git a/geocode-glib/geocode-glib-private.h b/geocode-glib/geocode-glib-private.h
index 06e9421..08fe408 100644
--- a/geocode-glib/geocode-glib-private.h
+++ b/geocode-glib/geocode-glib-private.h
@@ -34,8 +34,10 @@ typedef enum {
GEOCODE_GLIB_RESOLVE_REVERSE
} GeocodeLookupType;
-GHashTable *_geocode_parse_json (const char *contents,
- GError **error);
+GHashTable *_geocode_parse_resolve_json (const char *contents,
+ GError **error);
+GList * _geocode_parse_search_json (const char *contents,
+ GError **error);
G_END_DECLS
diff --git a/geocode-glib/geocode-glib.c b/geocode-glib/geocode-glib.c
index 20c9620..92cea46 100644
--- a/geocode-glib/geocode-glib.c
+++ b/geocode-glib/geocode-glib.c
@@ -36,8 +36,10 @@
* @include: geocode-glib/geocode-glib.h
*
* Contains functions for geocoding and reverse geocoding using the
- * <ulink url="http://developer.yahoo.com/geo/placefinder/guide/requests.html">Yahoo! Place Finder APIs</ulink>.
- * Lookups will only ever return one result, or an error if the lookup failed.
+ * <ulink url="http://developer.yahoo.com/geo/placefinder/guide/requests.html">Yahoo! Place Finder APIs</ulink>
+ * for geocoding and reverse geocoding, and the
+ * <ulink url="http://developer.yahoo.com/geo/geoplanet/">Yahoo! GeoPlanet APIs</ulink>
+ * for forward geocoding searches.
**/
struct _GeocodeObjectPrivate {
@@ -366,8 +368,8 @@ geocode_object_add (GeocodeObject *object,
}
GHashTable *
-_geocode_parse_json (const char *contents,
- GError **error)
+_geocode_parse_resolve_json (const char *contents,
+ GError **error)
{
GHashTable *ret;
JsonParser *parser;
@@ -529,7 +531,7 @@ on_query_data_loaded (GObject *source_object,
return;
}
- ret = _geocode_parse_json (contents, &error);
+ ret = _geocode_parse_resolve_json (contents, &error);
if (ret == NULL) {
g_simple_async_result_set_from_error (simple, error);
@@ -579,7 +581,7 @@ on_cache_data_loaded (GObject *source_object,
return;
}
- ret = _geocode_parse_json (contents, &error);
+ ret = _geocode_parse_resolve_json (contents, &error);
g_free (contents);
if (ret == NULL) {
@@ -615,41 +617,38 @@ dup_ht (GHashTable *ht)
}
static GFile *
-get_query_for_params (GeocodeObject *object)
+get_resolve_query_for_params (GeocodeObject *object)
{
GFile *ret;
+ GHashTable *ht;
+ char *locale;
+ char *params, *uri;
- if (object->priv->type == GEOCODE_GLIB_RESOLVE_FORWARD ||
- object->priv->type == GEOCODE_GLIB_RESOLVE_REVERSE) {
- GHashTable *ht;
- char *locale;
- char *params, *uri;
+ g_return_val_if_fail (object->priv->type == GEOCODE_GLIB_RESOLVE_FORWARD ||
+ object->priv->type == GEOCODE_GLIB_RESOLVE_REVERSE,
+ NULL);
- ht = dup_ht (object->priv->ht);
+ ht = dup_ht (object->priv->ht);
- g_hash_table_insert (ht, "appid", YAHOO_APPID);
- g_hash_table_insert (ht, "flags", "QJT");
+ g_hash_table_insert (ht, "appid", YAHOO_APPID);
+ g_hash_table_insert (ht, "flags", "QJT");
- if (object->priv->type == GEOCODE_GLIB_RESOLVE_REVERSE)
- g_hash_table_insert (ht, "gflags", "R");
+ if (object->priv->type == GEOCODE_GLIB_RESOLVE_REVERSE)
+ g_hash_table_insert (ht, "gflags", "R");
- locale = geocode_object_get_lang ();
- if (locale)
- g_hash_table_insert (ht, "locale", locale);
+ locale = geocode_object_get_lang ();
+ if (locale)
+ g_hash_table_insert (ht, "locale", locale);
- params = soup_form_encode_hash (ht);
- g_hash_table_destroy (ht);
- g_free (locale);
+ params = soup_form_encode_hash (ht);
+ g_hash_table_destroy (ht);
+ g_free (locale);
- uri = g_strdup_printf ("http://where.yahooapis.com/geocode?%s", params);
- g_free (params);
+ uri = g_strdup_printf ("http://where.yahooapis.com/geocode?%s", params);
+ g_free (params);
- ret = g_file_new_for_uri (uri);
- g_free (uri);
- } else {
- g_warning ("Lookup type (%d) not supported", object->priv->type);
- ret = NULL;
- }
+ ret = g_file_new_for_uri (uri);
+ g_free (uri);
return ret;
}
@@ -685,7 +684,7 @@ geocode_object_resolve_async (GeocodeObject *object,
user_data,
geocode_object_resolve_async);
- query = get_query_for_params (object);
+ query = get_resolve_query_for_params (object);
cache_path = geocode_object_cache_path_for_query (query);
if (cache_path == NULL) {
g_file_load_contents_async (query,
@@ -767,7 +766,7 @@ geocode_object_resolve (GeocodeObject *object,
g_return_val_if_fail (GEOCODE_IS_OBJECT (object), NULL);
- query = get_query_for_params (object);
+ query = get_resolve_query_for_params (object);
if (geocode_object_cache_load (query, &contents) == FALSE) {
if (g_file_load_contents (query,
NULL,
@@ -781,7 +780,7 @@ geocode_object_resolve (GeocodeObject *object,
to_cache = TRUE;
}
- ret = _geocode_parse_json (contents, error);
+ ret = _geocode_parse_resolve_json (contents, error);
if (to_cache)
geocode_object_cache_save (query, contents);
@@ -791,6 +790,313 @@ geocode_object_resolve (GeocodeObject *object,
return ret;
}
+static GFile *
+get_search_query_for_params (GeocodeObject *object)
+{
+ GFile *ret;
+ GHashTable *ht;
+ char *lang;
+ char *params;
+ char *search_term;
+ char *uri;
+ const char *location;
+
+ g_return_val_if_fail (object->priv->type == GEOCODE_GLIB_RESOLVE_FORWARD, NULL);
+ location = g_hash_table_lookup (object->priv->ht, "location");
+ g_return_val_if_fail (location != NULL, NULL);
+
+ /* Prepare the search term */
+ search_term = soup_uri_encode (location, NULL);
+
+ /* Prepare the query parameters */
+ ht = g_hash_table_new (g_str_hash, g_str_equal);
+ g_hash_table_insert (ht, "appid", YAHOO_APPID);
+ g_hash_table_insert (ht, "format", "json");
+ lang = geocode_object_get_lang ();
+ if (lang)
+ g_hash_table_insert (ht, "lang", lang);
+
+ params = soup_form_encode_hash (ht);
+ g_hash_table_destroy (ht);
+ g_free (lang);
+
+ /* XXX: Make count a property? */
+ uri = g_strdup_printf ("http://where.yahooapis.com/v1/places.q('%s');start=0;count=10?%s", search_term, params);
+ g_free (params);
+ g_free (search_term);
+
+ ret = g_file_new_for_uri (uri);
+ g_free (uri);
+
+ return ret;
+}
+
+/**
+ * geocode_object_search_async:
+ * @object: a #GeocodeObject representing a query
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @callback: a #GAsyncReadyCallback to call when the request is satisfied
+ * @user_data: the data to pass to callback function
+ *
+ * Asynchronously gets a list of a geocoding
+ * query using a web service. Use geocode_object_search() to do the same
+ * thing synchronously.
+ *
+ * When the operation is finished, @callback will be called. You can then call
+ * geocode_object_search_finish() to get the result of the operation.
+ **/
+void
+geocode_object_search_async (GeocodeObject *object,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *simple;
+ GFile *query;
+ char *cache_path;
+
+ g_return_if_fail (GEOCODE_IS_OBJECT (object));
+
+ simple = g_simple_async_result_new (G_OBJECT (object),
+ callback,
+ user_data,
+ geocode_object_search_async);
+
+ g_simple_async_result_set_error (simple,
+ GEOCODE_ERROR,
+ GEOCODE_ERROR_NOT_SUPPORTED,
+ "XXX NOT IMPLEMENTED XXX");
+ g_simple_async_result_complete_in_idle (simple);
+
+#if 0
+ query = get_search_query_for_params (object);
+ cache_path = geocode_object_cache_path_for_query (query);
+ if (cache_path == NULL) {
+ g_file_load_contents_async (query,
+ cancellable,
+ on_query_data_loaded,
+ simple);
+ g_object_unref (query);
+ } else {
+ GFile *cache;
+
+ cache = g_file_new_for_path (cache_path);
+ g_object_set_data_full (G_OBJECT (cache), "query", query, (GDestroyNotify) g_object_unref);
+ g_object_set_data (G_OBJECT (cache), "cancellable", cancellable);
+ g_file_load_contents_async (cache,
+ cancellable,
+ on_cache_data_loaded,
+ simple);
+ g_object_unref (cache);
+ }
+#endif
+}
+
+/**
+ * geocode_object_search_finish:
+ * @object: a #GeocodeObject representing a query
+ * @res: a #GAsyncResult.
+ * @error: a #GError.
+ *
+ * Finishes a query operation. See geocode_object_search_async().
+ *
+ * Returns: (element-type GHashTable) (transfer full):
+ * a #GHashTable containing the results of the query
+ * or %NULL in case of errors.
+ * Free the returned string with g_hash_table_destroy() when done.
+ **/
+GList *
+geocode_object_search_finish (GeocodeObject *object,
+ GAsyncResult *res,
+ GError **error)
+{
+ GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
+ GHashTable *ret;
+
+ g_return_val_if_fail (GEOCODE_IS_OBJECT (object), NULL);
+
+ g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == geocode_object_search_async);
+
+ ret = NULL;
+
+ if (g_simple_async_result_propagate_error (simple, error))
+ goto out;
+
+ ret = g_simple_async_result_get_op_res_gpointer (simple);
+
+out:
+ return ret;
+}
+
+#define IS_EL(x) (g_str_equal (element_name, x))
+
+static void
+insert_place_attr (GHashTable *ht,
+ JsonReader *reader,
+ const char *element_name)
+{
+ char *value;
+
+ if (json_reader_read_member (reader, element_name) == FALSE) {
+ json_reader_end_member (reader);
+ return;
+ }
+
+ /* FIXME: check all the member names against what Place Finder outputs */
+
+ if (IS_EL("woeid") ||
+ IS_EL("popRank") ||
+ IS_EL("areaRank")) {
+ value = g_strdup_printf ("%"G_GINT64_FORMAT, json_reader_get_int_value (reader));
+ } else if (IS_EL("centroid")) {
+ json_reader_read_member (reader, "longitude");
+ g_hash_table_insert (ht, g_strdup ("longitude"),
+ g_strdup_printf ("%lf", json_reader_get_double_value (reader)));
+ json_reader_end_member (reader);
+ json_reader_read_member (reader, "latitude");
+ g_hash_table_insert (ht, g_strdup ("latitude"),
+ g_strdup_printf ("%lf", json_reader_get_double_value (reader)));
+ json_reader_end_member (reader);
+ goto end;
+ } else if (g_str_has_suffix (element_name, " attrs")) {
+ g_debug ("Ignoring attributes element '%s'", element_name);
+ value = g_strdup (""); /* So that they're ignored */
+ } else if (IS_EL("boundingBox")) {
+ g_debug ("Ignoring element '%s'", element_name);
+ value = g_strdup (""); /* So that they're ignored */
+ } else {
+ value = g_strdup (json_reader_get_string_value (reader));
+ }
+
+ if (value != NULL && *value == '\0') {
+ g_clear_pointer (&value, g_free);
+ goto end;
+ }
+
+ if (value != NULL)
+ g_hash_table_insert (ht, g_strdup (element_name), value);
+ else
+ g_warning ("Ignoring element %s, don't know how to parse it", element_name);
+
+end:
+ json_reader_end_member (reader);
+}
+
+GList *
+_geocode_parse_search_json (const char *contents,
+ GError **error)
+{
+ GList *ret;
+ JsonParser *parser;
+ JsonNode *root;
+ JsonReader *reader;
+ const GError *err = NULL;
+ int num_places, i;
+
+ ret = NULL;
+
+ parser = json_parser_new ();
+ if (json_parser_load_from_data (parser, contents, -1, error) == FALSE) {
+ g_object_unref (parser);
+ return ret;
+ }
+
+ root = json_parser_get_root (parser);
+ reader = json_reader_new (root);
+
+ if (json_reader_read_member (reader, "places") == FALSE)
+ goto parse;
+ if (json_reader_read_member (reader, "place") == FALSE)
+ goto parse;
+
+ num_places = json_reader_count_elements (reader);
+ if (num_places < 0)
+ goto parse;
+
+ for (i = 0; i < num_places; i++) {
+ GHashTable *ht;
+ char **members;
+ int j;
+
+ json_reader_read_element (reader, i);
+
+ ht = g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free, g_free);
+
+ members = json_reader_list_members (reader);
+ for (j = 0; members != NULL && members[j] != NULL; j++)
+ insert_place_attr (ht, reader, members[j]);
+ g_strfreev (members);
+
+ json_reader_end_element (reader);
+
+ ret = g_list_prepend (ret, ht);
+ }
+
+ g_object_unref (parser);
+ g_object_unref (reader);
+ ret = g_list_reverse (ret);
+
+ return ret;
+parse:
+ err = json_reader_get_error (reader);
+ g_set_error_literal (error, GEOCODE_ERROR, GEOCODE_ERROR_PARSE, err->message);
+ g_object_unref (parser);
+ g_object_unref (reader);
+ return NULL;
+}
+
+/**
+ * geocode_object_search:
+ * @object: a #GeocodeObject representing a query
+ * @error: a #GError
+ *
+ * Gets the result of a geocoding or reverse geocoding
+ * query using a web service.
+ *
+ * Returns: (element-type GHashTable) (transfer full):
+ * a #GHashTable containing the results of the query
+ * or %NULL in case of errors.
+ * Free the returned string with g_hash_table_destroy() when done.
+ **/
+GList *
+geocode_object_search (GeocodeObject *object,
+ GError **error)
+{
+ GFile *query;
+ char *contents;
+ GList *ret;
+ gboolean to_cache = FALSE;
+
+ g_return_val_if_fail (GEOCODE_IS_OBJECT (object), NULL);
+
+ query = get_search_query_for_params (object);
+ if (geocode_object_cache_load (query, &contents) == FALSE) {
+ if (g_file_load_contents (query,
+ NULL,
+ &contents,
+ NULL,
+ NULL,
+ error) == FALSE) {
+ /* FIXME check error value and match against
+ * web service errors:
+ * http://developer.yahoo.com/geo/geoplanet/guide/api_docs.html#response-errors */
+ g_object_unref (query);
+ return NULL;
+ }
+ to_cache = TRUE;
+ }
+
+ ret = _geocode_parse_search_json (contents, error);
+ if (to_cache && ret != NULL)
+ geocode_object_cache_save (query, contents);
+
+ g_free (contents);
+ g_object_unref (query);
+
+ return ret;
+}
+
/**
* geocode_object_get_coords:
* @results: a #GHashTable as generated by geocode_object_resolve()
diff --git a/geocode-glib/geocode-glib.h b/geocode-glib/geocode-glib.h
index 4e9776c..9ad5009 100644
--- a/geocode-glib/geocode-glib.h
+++ b/geocode-glib/geocode-glib.h
@@ -280,6 +280,18 @@ GHashTable *geocode_object_resolve_finish (GeocodeObject *object,
GHashTable * geocode_object_resolve (GeocodeObject *object,
GError **error);
+void geocode_object_search_async (GeocodeObject *object,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+
+GList *geocode_object_search_finish (GeocodeObject *object,
+ GAsyncResult *res,
+ GError **error);
+
+GList * geocode_object_search (GeocodeObject *object,
+ GError **error);
+
gboolean geocode_object_get_coords (GHashTable *results,
gdouble *longitude,
gdouble *latitude);
diff --git a/geocode-glib/geocode-glib.symbols b/geocode-glib/geocode-glib.symbols
index c01323f..13933f4 100644
--- a/geocode-glib/geocode-glib.symbols
+++ b/geocode-glib/geocode-glib.symbols
@@ -8,5 +8,9 @@ geocode_object_add
geocode_object_resolve_async
geocode_object_resolve_finish
geocode_object_resolve
+geocode_object_search_async
+geocode_object_search_finish
+geocode_object_search
geocode_object_get_coords
-_geocode_parse_json
+_geocode_parse_search_json
+_geocode_parse_resolve_json
diff --git a/geocode-glib/test-gcglib.c b/geocode-glib/test-gcglib.c
index e69b4eb..57cfe40 100644
--- a/geocode-glib/test-gcglib.c
+++ b/geocode-glib/test-gcglib.c
@@ -209,7 +209,7 @@ test_json (void)
};
for (i = 0; i < G_N_ELEMENTS (tests); i++) {
- ht = _geocode_parse_json (tests[i].test, &error);
+ ht = _geocode_parse_resolve_json (tests[i].test, &error);
if (tests[i].error) {
g_assert (ht == NULL);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]