[geocode-glib/wip/place-details: 3/3] lib: geocode_forward_search*() returns places



commit 297d8bd39f081e2bd3ecf19f399daae5d3fe79e7
Author: Zeeshan Ali (Khattak) <zeeshanak gnome org>
Date:   Thu Apr 25 04:45:10 2013 +0300

    lib: geocode_forward_search*() returns places
    
    geocode_forward_search*() now return list of GeocodePlace objects rather
    than simple GeocodeLocation objects.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=697405

 geocode-glib/geocode-forward.c |  269 +++++++++++++++++++---------------------
 geocode-glib/test-gcglib.c     |  105 +++++++++++++---
 2 files changed, 211 insertions(+), 163 deletions(-)
---
diff --git a/geocode-glib/geocode-forward.c b/geocode-glib/geocode-forward.c
index da1461a..9faecb5 100644
--- a/geocode-glib/geocode-forward.c
+++ b/geocode-glib/geocode-forward.c
@@ -98,6 +98,8 @@ _geocode_parse_single_result_json (const char  *contents,
                                   GError     **error)
 {
        GHashTable *ht;
+        GeocodePlace *place = NULL;
+        char *name;
        GeocodeLocation *loc;
        gdouble longitude;
        gdouble latitude;
@@ -108,13 +110,17 @@ _geocode_parse_single_result_json (const char  *contents,
 
        longitude = g_ascii_strtod (g_hash_table_lookup (ht, "longitude"), NULL);
        latitude = g_ascii_strtod (g_hash_table_lookup (ht, "latitude"), NULL);
-       loc = geocode_location_new_with_description (longitude,
-                                                 latitude,
-                                                 GEOCODE_LOCATION_ACCURACY_UNKNOWN,
-                                                 g_hash_table_lookup (ht, "line2"));
+        name = g_hash_table_lookup (ht, "line2");
+        loc = geocode_location_new_with_description (longitude,
+                                                     latitude,
+                                                     GEOCODE_LOCATION_ACCURACY_UNKNOWN,
+                                                     name);
+        place = geocode_place_new_with_location (name, GEOCODE_PLACE_TYPE_UNKNOWN, loc);
+
+        g_object_unref (loc);
        g_hash_table_destroy (ht);
 
-       return g_list_append (NULL, loc);
+        return g_list_append (NULL, place);
 }
 
 static struct {
@@ -472,8 +478,8 @@ geocode_forward_search_async (GeocodeForward      *forward,
  *
  * Finishes a forward geocoding operation. See geocode_forward_search_async().
  *
- * Returns: (element-type GeocodeLocation) (transfer container): A list of
- * locations or %NULL in case of errors. Free the returned list with
+ * Returns: (element-type GeocodePlace) (transfer container): A list of
+ * places or %NULL in case of errors. Free the returned list with
  * g_list_free() when done.
  **/
 GList *
@@ -533,11 +539,27 @@ insert_place_attr (GHashTable *ht,
                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 */
+                if (IS_EL("placeTypeName attrs")) {
+                    char *code;
+
+                    json_reader_read_member (reader, "code");
+                    code = g_strdup_printf ("%" G_GINT64_FORMAT, json_reader_get_int_value (reader));
+                    g_hash_table_insert (ht, g_strdup ("placeType"), code);
+                    json_reader_end_member (reader);
+                    goto end;
+                } else {
+                    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 if (IS_EL("placeTypeName")) {
+                /* This string is also localized so we want the code rather
+                 * than name, which we extract above.
+                 */
+                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));
        }
@@ -556,131 +578,105 @@ end:
        json_reader_end_member (reader);
 }
 
-static gboolean
-node_free_func (GNode    *node,
-               gpointer  user_data)
-{
-       /* Leaf nodes are GeocodeLocation objects
-        * which we reuse for the results */
-       if (G_NODE_IS_LEAF (node) == FALSE)
-               g_free (node->data);
-
-       return FALSE;
-}
-
-#define N_ATTRS 7
-static const char const *attributes[7] = {
-       "country",
-       "admin1",
-       "admin2",
-       "admin3",
-       "postal",
-       "placeTypeName",
-       "locality1"
+static struct {
+        const char *yahoo_attr;
+        const char *place_prop; /* NULL to ignore */
+} yahoo_to_place_map[] = {
+        { "postal", "postal-code" },
+        { "locality1", "town" },
+        { "admin1", "state" },
+        { "admin2", "county" },
+        { "admin3", "administrative-area" },
+        { "country", "country" },
 };
 
 static void
-insert_place_into_tree (GNode *location_tree, GHashTable *ht)
+fill_place_from_entry (const char *key,
+                       const char *value,
+                       GeocodePlace *place)
 {
-       GNode *start = location_tree, *child = NULL;
-       GeocodeLocation *loc = NULL;
-       char *attr_val = NULL;
-       char *name;
-       gdouble longitude, latitude;
-       guint i;
-
-       for (i = 0; i < G_N_ELEMENTS(attributes); i++) {
-               attr_val = g_hash_table_lookup (ht, attributes[i]);
-               if (!attr_val) {
-                       /* Add a dummy node if the attribute value is not
-                        * available for the place */
-                       child = g_node_insert_data (start, -1, NULL);
-               } else {
-                       /* If the attr value (eg for country United States)
-                        * already exists, then keep on adding other attributes under that node. */
-                       child = g_node_first_child (start);
-                       while (child &&
-                              child->data &&
-                              g_ascii_strcasecmp (child->data, attr_val) != 0) {
-                               child = g_node_next_sibling (child);
-                       }
-                       if (!child) {
-                               /* create a new node */
-                               child = g_node_insert_data (start, -1, g_strdup (attr_val));
-                       }
-               }
-               start = child;
-       }
-
-       /* Get latitude and longitude and create GeocodeLocation object.
-        * The leaf node of the tree is the GeocodeLocation object */
-       longitude = g_ascii_strtod (g_hash_table_lookup (ht, "longitude"), NULL);
-       latitude = g_ascii_strtod (g_hash_table_lookup (ht, "latitude"), NULL);
-       name = g_hash_table_lookup (ht, "name");
-
-       loc = geocode_location_new_with_description (latitude,
-                                                 longitude,
-                                                 GEOCODE_LOCATION_ACCURACY_UNKNOWN,
-                                                 name);
+        guint i;
+
+
+        for (i = 0; i < G_N_ELEMENTS (yahoo_to_place_map); i++) {
+                if (g_str_equal (key, yahoo_to_place_map[i].yahoo_attr)){
+                        g_object_set (G_OBJECT (place),
+                                      yahoo_to_place_map[i].place_prop,
+                                      value,
+                                      NULL);
+                        break;
+                }
+        }
+}
 
-       g_node_insert_data (start, -1, loc);
+static char *
+create_description_from_place (GeocodePlace *place)
+{
+        GString *description;
+        const char *name, *state, *admin_area, *country;
+
+        name = geocode_place_get_name (place);
+        description = g_string_new (name);
+        state = geocode_place_get_state (place);
+        admin_area = geocode_place_get_administrative_area (place);
+        /* Avoid appending duplicate names, e.g in case of Rio de Janeiro, which
+         * is both a town and state. */
+        if (state != NULL &&
+            g_strcmp0 (state, name) != 0) {
+                g_string_append (description, ", ");
+                g_string_append (description, state);
+        } else if (admin_area != NULL &&
+                   g_strcmp0 (admin_area, name) != 0) {
+                g_string_append (description, ", ");
+                g_string_append (description, admin_area);
+        }
+
+        country = geocode_place_get_country (place);
+        if (country != NULL) {
+                g_string_append (description, ", ");
+                g_string_append (description, country);
+        }
+
+        return g_string_free (description, FALSE);
 }
 
 static void
-make_location_list_from_tree (GNode   *node,
-                             char   **s_array,
-                             GList  **location_list,
-                             int      i)
+insert_place_into_list (GList **place_list, GHashTable *ht)
 {
-       GNode *child;
-       gboolean add_attribute = FALSE;
-
-       if (node == NULL)
-               return;
-
-       if (G_NODE_IS_LEAF (node)) {
-               GPtrArray *rev_s_array;
-               GeocodeLocation *loc;
-               const char *name;
-               char *description;
-               int counter = 0;
-
-               rev_s_array = g_ptr_array_new ();
-
-               /* If leaf node, then add all the attributes in the s_array
-                * and set it to the description of the loc object */
-               loc = (GeocodeLocation *) node->data;
-
-               name = geocode_location_get_description (loc);
-
-               /* To print the attributes in a meaningful manner
-                * reverse the s_array */
-               g_ptr_array_add (rev_s_array, (gpointer) name);
-               for (counter = 1; counter <= i; counter++)
-                       g_ptr_array_add (rev_s_array, s_array[i - counter]);
-               g_ptr_array_add (rev_s_array, NULL);
-               description = g_strjoinv (", ", (char **) rev_s_array->pdata);
-               g_ptr_array_unref (rev_s_array);
-
-               geocode_location_set_description (loc, description);
-               g_free (description);
-
-               *location_list = g_list_prepend (*location_list, loc);
-       } else {
-               /* If there are other attributes with a different value,
-                * add those attributes to the string to differentiate them */
-               if (g_node_prev_sibling (node) ||
-                  g_node_next_sibling (node))
-                       add_attribute = TRUE;
-
-               if (add_attribute) {
-                       s_array[i] = node->data;
-                       i++;
-               }
-       }
-
-       for (child = node->children; child != NULL; child = child->next)
-               make_location_list_from_tree (child, s_array, location_list, i);
+        GeocodePlaceType place_type;
+        GeocodePlace *place = NULL;
+        GeocodeLocation *loc = NULL;
+        char *name, *description;
+        gdouble longitude, latitude;
+
+        name = g_hash_table_lookup (ht, "name");
+        place_type = atoi (g_hash_table_lookup (ht, "placeType"));
+        place = geocode_place_new (name, place_type);
+
+        g_hash_table_foreach (ht, (GHFunc) fill_place_from_entry, place);
+
+        /* Yahoo API doesn't give us street addresses so we'll just have to
+         * live with only street name for now. */
+        if (place_type == GEOCODE_PLACE_TYPE_SUBURB) {
+            const char *street = g_hash_table_lookup (ht, "locality2");
+            if (street != NULL)
+                geocode_place_set_street_address (place, street);
+        }
+
+        /* Get latitude and longitude and create GeocodeLocation object. */
+        longitude = g_ascii_strtod (g_hash_table_lookup (ht, "longitude"), NULL);
+        latitude = g_ascii_strtod (g_hash_table_lookup (ht, "latitude"), NULL);
+        description = create_description_from_place (place);
+
+        loc = geocode_location_new_with_description (latitude,
+                                                     longitude,
+                                                     GEOCODE_LOCATION_ACCURACY_UNKNOWN,
+                                                     description);
+        g_free (description);
+        geocode_place_set_location (place, loc);
+        g_object_unref (loc);
+
+        *place_list = g_list_prepend (*place_list, place);
 }
 
 GList *
@@ -693,8 +689,6 @@ _geocode_parse_search_json (const char *contents,
        JsonReader *reader;
        const GError *err = NULL;
        int num_places, i;
-       GNode *location_tree;
-       char *s_array[N_ATTRS];
 
        ret = NULL;
 
@@ -716,8 +710,6 @@ _geocode_parse_search_json (const char *contents,
        if (num_places < 0)
                goto parse;
 
-       location_tree = g_node_new (NULL);
-
        for (i = 0; i < num_places; i++) {
                GHashTable *ht;
                char **members;
@@ -732,8 +724,8 @@ _geocode_parse_search_json (const char *contents,
                for (j = 0; members != NULL && members[j] != NULL; j++)
                        insert_place_attr (ht, reader, members[j]);
 
-               /* Populate the tree with place details */
-               insert_place_into_tree (location_tree, ht);
+                /* Populate the list with place details */
+                insert_place_into_list (&ret, ht);
 
                g_hash_table_destroy (ht);
                g_strfreev (members);
@@ -741,17 +733,6 @@ _geocode_parse_search_json (const char *contents,
                json_reader_end_element (reader);
        }
 
-       make_location_list_from_tree (location_tree, s_array, &ret, 0);
-
-       g_node_traverse (location_tree,
-                        G_IN_ORDER,
-                        G_TRAVERSE_ALL,
-                        -1,
-                        (GNodeTraverseFunc) node_free_func,
-                        NULL);
-
-       g_node_destroy (location_tree);
-
        g_object_unref (parser);
        g_object_unref (reader);
        ret = g_list_reverse (ret);
@@ -773,8 +754,8 @@ parse:
  * Gets the result of a forward geocoding
  * query using a web service.
  *
- * Returns: (element-type GeocodeLocation) (transfer container): A list of
- * locations or %NULL in case of errors. Free the returned list with
+ * Returns: (element-type GeocodePlace) (transfer container): A list of
+ * places or %NULL in case of errors. Free the returned list with
  * g_list_free() when done.
  **/
 GList *
diff --git a/geocode-glib/test-gcglib.c b/geocode-glib/test-gcglib.c
index e90a1f7..901f240 100644
--- a/geocode-glib/test-gcglib.c
+++ b/geocode-glib/test-gcglib.c
@@ -22,6 +22,15 @@ print_loc (GeocodeLocation *loc)
 }
 
 static void
+print_place (GeocodePlace *place)
+{
+       /* For now just print the underlying location */
+       GeocodeLocation *loc = geocode_place_get_location (place);
+
+       print_loc (loc);
+}
+
+static void
 print_res (const char *key,
           const char *value,
           gpointer    data)
@@ -71,11 +80,11 @@ got_geocode_search_cb (GObject *source_object,
        }
 
        for (l = results; l != NULL; l = l->next) {
-               GeocodeLocation *loc = l->data;
+               GeocodePlace *place = l->data;
 
                g_print ("Got geocode search answer:\n");
-               print_loc (loc);
-               g_object_unref (loc);
+               print_place (place);
+               g_object_unref (place);
        }
        g_list_free (results);
 
@@ -129,6 +138,7 @@ test_xep (void)
        GHashTable *tp;
        GeocodeForward *object;
        GList *res;
+       GeocodePlace *place;
        GeocodeLocation *loc;
        GError *error = NULL;
 
@@ -155,11 +165,13 @@ test_xep (void)
 
        g_object_unref (object);
 
-       loc = res->data;
+       place = res->data;
+       loc = geocode_place_get_location (place);
+       g_assert (loc != NULL);
        g_assert_cmpfloat (geocode_location_get_latitude (loc), ==, -0.589669);
        g_assert_cmpfloat (geocode_location_get_longitude (loc), ==, 51.237070);
 
-       g_object_unref (loc);
+       g_object_unref (place);
        g_list_free (res);
 }
 
@@ -169,6 +181,7 @@ test_pub (void)
        GeocodeForward *object;
        GError *error = NULL;
        GList *res;
+       GeocodePlace *place;
        GeocodeLocation *loc;
 
        object = geocode_forward_new_for_string ("9, old palace road, guildford, surrey");
@@ -183,12 +196,14 @@ test_pub (void)
        g_object_unref (object);
 
        g_assert_cmpint (g_list_length (res), ==, 1);
-       loc = res->data;
+       place = res->data;
+       loc = geocode_place_get_location (place);
+       g_assert (loc != NULL);
 
        g_assert_cmpfloat (geocode_location_get_latitude (loc), ==, -0.589669);
        g_assert_cmpfloat (geocode_location_get_longitude (loc), ==, 51.237070);
 
-       g_object_unref (loc);
+       g_object_unref (place);
        g_list_free (res);
 }
 
@@ -221,14 +236,26 @@ test_search (void)
        got_france = FALSE;
        got_texas = FALSE;
        for (l = results; l != NULL; l = l->next) {
-               GeocodeLocation *loc = l->data;
+               GeocodeLocation *loc;
+               GeocodePlace *place = l->data;
+               g_assert (g_strcmp0 (geocode_place_get_name (place), "Paris") == 0);
+
+               loc = geocode_place_get_location (place);
+               g_assert (loc != NULL);
 
-               if (g_strcmp0 (geocode_location_get_description (loc), "Paris, France") == 0)
+               if (g_strcmp0 (geocode_place_get_state (place), "Ile-de-France") == 0 &&
+                   g_strcmp0 (geocode_place_get_country (place), "France") == 0 &&
+                   g_strcmp0 (geocode_location_get_description (loc),
+                              "Paris, Ile-de-France, France") == 0)
                        got_france = TRUE;
-               else if (g_strcmp0 (geocode_location_get_description (loc), "Paris, Texas, United States") == 
0)
+               else if (g_strcmp0 (geocode_place_get_state (place), "Texas") == 0 &&
+                        g_strcmp0 (geocode_place_get_country (place),
+                                   "United States") == 0 &&
+                        g_strcmp0 (geocode_location_get_description (loc),
+                                   "Paris, Texas, United States") == 0)
                        got_texas = TRUE;
 
-               g_object_unref (loc);
+               g_object_unref (place);
 
                if (got_france && got_texas)
                        break;
@@ -248,6 +275,7 @@ test_search_lat_long (void)
        GeocodeForward *object;
        GError *error = NULL;
        GList *res;
+       GeocodePlace *place;
        GeocodeLocation *loc;
 
        object = geocode_forward_new_for_string ("Santa María del Río");
@@ -259,9 +287,20 @@ test_search_lat_long (void)
        g_assert (res != NULL);
        g_object_unref (object);
 
-       loc = res->data;
+       place = res->data;
+       loc = geocode_place_get_location (place);
+       g_assert (loc != NULL);
+
        g_assert_cmpfloat (geocode_location_get_latitude (loc) - 21.800699, <, 0.000001);
        g_assert_cmpfloat (geocode_location_get_longitude (loc) - -100.735626, <, 0.000001);
+       g_assert_cmpstr (geocode_place_get_name (place), ==, "Santa Maria Del Rio");
+       g_assert_cmpstr (geocode_place_get_town (place), ==, "Santa Maria Del Rio");
+       g_assert_cmpstr (geocode_place_get_state (place), ==, "San Luis Potosi");
+       g_assert_cmpstr (geocode_place_get_county (place), ==, "Santa Maria del Rio");
+       g_assert_cmpstr (geocode_place_get_country (place), ==, "Mexico");
+       g_assert_cmpstr (geocode_location_get_description (loc),
+                        ==,
+                        "Santa Maria Del Rio, San Luis Potosi, Mexico");
 
        g_list_free_full (res, (GDestroyNotify) g_object_unref);
 }
@@ -287,6 +326,7 @@ test_locale (void)
        GeocodeForward *object;
        GError *error = NULL;
        GList *res;
+       GeocodePlace *place;
        GeocodeLocation *loc;
        char *old_locale;
 
@@ -303,11 +343,18 @@ test_locale (void)
        g_assert (res != NULL);
        g_object_unref (object);
 
-       loc = res->data;
-       g_assert_cmpstr (geocode_location_get_description (loc), ==, "Moskva, Rusko");
+       place = res->data;
+       g_assert_cmpstr (geocode_place_get_name (place), ==, "Moskva");
+       /* For some reason, Yahoo doesn't localise the state's name in this case */
+       g_assert_cmpstr (geocode_place_get_state (place), ==, "Moscow Federal City");
+       g_assert_cmpstr (geocode_place_get_country (place), ==, "Rusko");
+
+       loc = geocode_place_get_location (place);
+       g_assert (loc != NULL);
+       g_assert_cmpstr (geocode_location_get_description (loc), ==, "Moskva, Moscow Federal City, Rusko");
        g_assert_cmpfloat (geocode_location_get_latitude (loc) - 55.756950, <, 0.000001);
        g_assert_cmpfloat (geocode_location_get_longitude (loc) - 37.614971, <, 0.000001);
-       print_loc (loc);
+       print_place (place);
 
        g_list_free_full (res, (GDestroyNotify) g_object_unref);
 
@@ -322,9 +369,20 @@ test_locale (void)
        g_assert (res != NULL);
        g_object_unref (object);
 
-       loc = res->data;
-       g_assert_cmpstr (geocode_location_get_description (loc), ==, "Bonneville, Rhône-Alpes, France");
-       print_loc (loc);
+       place = res->data;
+       loc = geocode_place_get_location (place);
+       g_assert (loc != NULL);
+
+       g_assert_cmpstr (geocode_place_get_name (place), ==, "Bonneville");
+       g_assert_cmpstr (geocode_place_get_town (place), ==, "Bonneville");
+       g_assert_cmpstr (geocode_place_get_state (place), ==, "Rhône-Alpes");
+       g_assert_cmpstr (geocode_place_get_county (place), ==, "Haute-Savoie");
+       g_assert_cmpstr (geocode_place_get_administrative_area (place), ==, "Bonneville");
+       g_assert_cmpstr (geocode_place_get_country (place), ==, "France");
+       g_assert_cmpstr (geocode_location_get_description (loc),
+                        ==,
+                        "Bonneville, Rhône-Alpes, France");
+       print_place (place);
 
        g_list_free_full (res, (GDestroyNotify) g_object_unref);
 
@@ -388,6 +446,7 @@ test_search_json (void)
        GError *error = NULL;
        GList *list;
        char *contents;
+       GeocodePlace *place;
        GeocodeLocation *loc;
 
        if (g_file_get_contents (TEST_SRCDIR "/geoplanet-rio.json",
@@ -400,7 +459,15 @@ test_search_json (void)
        g_assert (list != NULL);
        g_assert_cmpint (g_list_length (list), ==, 10);
 
-       loc = list->data;
+       place = list->data;
+       loc = geocode_place_get_location (place);
+       g_assert (loc != NULL);
+
+       g_assert_cmpstr (geocode_place_get_name (place), ==, "Rio de Janeiro");
+       g_assert_cmpstr (geocode_place_get_town (place), ==, "Rio de Janeiro");
+       g_assert_cmpstr (geocode_place_get_state (place), ==, "Rio de Janeiro");
+       g_assert_cmpstr (geocode_place_get_county (place), ==, "Rio de Janeiro");
+       g_assert_cmpstr (geocode_place_get_country (place), ==, "Brazil");
        g_assert_cmpstr (geocode_location_get_description (loc), ==, "Rio de Janeiro, Brazil");
 
        g_list_free_full (list, (GDestroyNotify) g_object_unref);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]