Commit 2a8d8e40 authored by Philip Withnall's avatar Philip Withnall Committed by Philip Withnall
Browse files

geocode-backend: Add support for multiple reverse results

Some geocoding APIs (such as the Google Geocoding API) can return
multiple results for a reverse resolution request, such as the building
at that location, the street it is on, the town it is in, etc.

While we currently only support Nominatim, other backends may be added
(potentially out of tree) in future. Change the GeocodeBackend API to
allow them to return multiple reverse results.

This does not change the GeocodeReverse API, which returns the first
result in the list, which should be the most relevant one.

https://developers.google.com/maps/documentation/geocoding/intro#ReverseGeocoding

https://bugzilla.gnome.org/show_bug.cgi?id=756311
parent a2306cb8
......@@ -186,12 +186,14 @@ geocode_backend_reverse_resolve_async (GeocodeBackend *backend,
*
* Finishes a reverse geocoding operation. See geocode_backend_reverse_resolve_async().
*
* Returns: (transfer full): A #GeocodePlace instance, or %NULL in case of
* errors. Free the returned instance with g_object_unref() when done.
* Returns: (transfer full) (element-type GeocodePlace): A list of
* #GeocodePlace instances, or %NULL in case of errors. The list is ordered
* by relevance, with most relevant results first. Free the returned
* instances with g_object_unref() and the list with g_list_free() when done.
*
* Since: UNRELEASED
**/
GeocodePlace *
GList *
geocode_backend_reverse_resolve_finish (GeocodeBackend *backend,
GAsyncResult *result,
GError **error)
......@@ -214,14 +216,20 @@ geocode_backend_reverse_resolve_finish (GeocodeBackend *backend,
* @cancellable: optional #GCancellable object, %NULL to ignore.
* @error: a #GError.
*
* Gets the result of a reverse geocoding query using the @backend.
* Gets the result of a reverse geocoding query using the @backend. Typically, a
* single result will be returned representing the place at the given location;
* but in some cases the results will be ambiguous and multiple results will
* be returned. They will be returned in order of relevance, with the most
* relevant result first in the list.
*
* Returns: (transfer full): A #GeocodePlace instance, or %NULL in case of
* errors. Free the returned instance with g_object_unref() when done.
* Returns: (transfer full) (element-type GeocodePlace): A list of
* #GeocodePlace instances, or %NULL in case of errors. The list is ordered
* by relevance, with most relevant results first. Free the returned
* instances with g_object_unref() and the list with g_list_free() when done.
*
* Since: UNRELEASED
*/
GeocodePlace *
GList *
geocode_backend_reverse_resolve (GeocodeBackend *backend,
GeocodeLocation *location,
GCancellable *cancellable,
......@@ -305,14 +313,15 @@ reverse_resolve_async_thread (GTask *task,
GCancellable *cancellable)
{
GError *error = NULL;
GeocodePlace *place;
GList *places; /* (element-type GeocodePlace) */
place = geocode_backend_reverse_resolve (backend, location,
cancellable, &error);
places = geocode_backend_reverse_resolve (backend, location,
cancellable, &error);
if (error)
g_task_return_error (task, error);
else
g_task_return_pointer (task, place, g_object_unref);
g_task_return_pointer (task, places,
(GDestroyNotify) places_list_free);
}
static void
......@@ -330,7 +339,7 @@ real_reverse_resolve_async (GeocodeBackend *backend,
g_object_unref (task);
}
static GeocodePlace *
static GList *
real_reverse_resolve_finish (GeocodeBackend *backend,
GAsyncResult *result,
GError **error)
......
......@@ -77,7 +77,7 @@ struct _GeocodeBackendInterface
GError **error);
/* Reverse */
GeocodePlace *(*reverse_resolve) (GeocodeBackend *backend,
GList *(*reverse_resolve) (GeocodeBackend *backend,
GeocodeLocation *location,
GCancellable *cancellable,
GError **error);
......@@ -86,7 +86,7 @@ struct _GeocodeBackendInterface
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
GeocodePlace *(*reverse_resolve_finish) (GeocodeBackend *backend,
GList *(*reverse_resolve_finish) (GeocodeBackend *backend,
GAsyncResult *result,
GError **error);
......@@ -114,10 +114,10 @@ void geocode_backend_reverse_resolve_async (GeocodeBackend *back
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
GeocodePlace *geocode_backend_reverse_resolve_finish (GeocodeBackend *backend,
GList *geocode_backend_reverse_resolve_finish (GeocodeBackend *backend,
GAsyncResult *result,
GError **error);
GeocodePlace *geocode_backend_reverse_resolve (GeocodeBackend *backend,
GList *geocode_backend_reverse_resolve (GeocodeBackend *backend,
GeocodeLocation *location,
GCancellable *cancellable,
GError **error);
......
......@@ -984,12 +984,12 @@ _geocode_location_to_params (GeocodeLocation *location)
return ht;
}
static GeocodePlace *
static GList *
geocode_nominatim_reverse_resolve_finish (GeocodeBackend *backend,
GAsyncResult *res,
GError **error)
{
return GEOCODE_PLACE (g_task_propagate_pointer (G_TASK (res), error));
return g_task_propagate_pointer (G_TASK (res), error);
}
static void
......@@ -1149,6 +1149,12 @@ resolve_json (const char *contents,
return ret;
}
static void
places_list_free (GList *places)
{
g_list_free_full (places, g_object_unref);
}
static void
on_reverse_query_ready (GeocodeNominatim *self,
GAsyncResult *res,
......@@ -1156,7 +1162,7 @@ on_reverse_query_ready (GeocodeNominatim *self,
{
GError *error = NULL;
char *contents;
GeocodePlace *ret;
g_autoptr (GeocodePlace) place = NULL;
GHashTable *attributes;
contents = GEOCODE_NOMINATIM_GET_CLASS (self)->query_finish (GEOCODE_NOMINATIM (self), res, &error);
......@@ -1175,10 +1181,12 @@ on_reverse_query_ready (GeocodeNominatim *self,
return;
}
ret = _geocode_create_place_from_attributes (attributes);
place = _geocode_create_place_from_attributes (attributes);
g_hash_table_unref (attributes);
g_task_return_pointer (task, ret, g_object_unref);
g_task_return_pointer (task,
g_list_prepend (NULL, g_object_ref (place)),
(GDestroyNotify) places_list_free);
g_object_unref (task);
}
......@@ -1210,7 +1218,7 @@ geocode_nominatim_reverse_resolve_async (GeocodeBackend *self,
g_free (uri);
}
static GeocodePlace *
static GList *
geocode_nominatim_reverse_resolve (GeocodeBackend *self,
GeocodeLocation *location,
GCancellable *cancellable,
......@@ -1219,7 +1227,7 @@ geocode_nominatim_reverse_resolve (GeocodeBackend *self,
char *contents;
GHashTable *ht;
GHashTable *result = NULL;
GeocodePlace *place;
g_autoptr (GeocodePlace) place = NULL;
gchar *uri = NULL;
g_return_val_if_fail (GEOCODE_IS_BACKEND (self), NULL);
......@@ -1246,7 +1254,7 @@ geocode_nominatim_reverse_resolve (GeocodeBackend *self,
place = _geocode_create_place_from_attributes (result);
g_hash_table_unref (result);
return place;
return g_list_prepend (NULL, g_object_ref (place));
}
/******************************************************************************/
......
......@@ -109,20 +109,29 @@ ensure_backend (GeocodeReverse *object)
object->priv->backend = GEOCODE_BACKEND (geocode_nominatim_get_gnome ());
}
static void
places_list_free (GList *places)
{
g_list_free_full (places, g_object_unref);
}
static void
backend_reverse_resolve_ready (GeocodeBackend *backend,
GAsyncResult *res,
GTask *task)
{
GeocodePlace *place;
GList *places = NULL; /* (element-type GeocodePlace) */
GError *error = NULL;
place = geocode_backend_reverse_resolve_finish (backend, res, &error);
if (place)
g_task_return_pointer (task, place, g_object_unref);
/* Extract the first result from the list and return that. */
places = geocode_backend_reverse_resolve_finish (backend, res, &error);
if (places != NULL)
g_task_return_pointer (task, g_object_ref (places->data),
g_object_unref);
else
g_task_return_error (task, error);
g_object_unref (task);
g_clear_pointer (&places, places_list_free);
}
/**
......@@ -200,16 +209,26 @@ GeocodePlace *
geocode_reverse_resolve (GeocodeReverse *object,
GError **error)
{
GList *places = NULL; /* (element-type GeocodePlace) */
GeocodePlace *place = NULL;
g_return_val_if_fail (GEOCODE_IS_REVERSE (object), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
ensure_backend (object);
g_assert (object->priv->backend != NULL);
return geocode_backend_reverse_resolve (object->priv->backend,
object->priv->location,
NULL,
error);
places = geocode_backend_reverse_resolve (object->priv->backend,
object->priv->location,
NULL,
error);
if (places != NULL)
place = g_object_ref (places->data);
g_list_free_full (places, g_object_unref);
return place;
}
/**
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment