/*
* Copyright 2015 Canonical Ltd.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 3, as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranties of
* MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see .
*
* Authors:
* Lars Uebernickel
*/
#include "geonames.h"
#include "geonames-query.h"
/**
* SECTION: geonames
* @title: Geonames
* @short_description: Access the geonames.org database
* @include: geonames.h
*
* This library provides access to a local copy of a subset of the city
* and country data of geonames.org.
*/
static GVariant *geonames_data;
static void
ensure_geonames_data (void)
{
if (g_once_init_enter (&geonames_data))
{
g_autoptr(GBytes) data;
GVariant *v;
data = g_resources_lookup_data ("/com/ubuntu/geonames/cities.compiled", G_RESOURCE_LOOKUP_FLAGS_NONE, NULL);
g_assert (data);
v = g_variant_new_from_bytes (G_VARIANT_TYPE ("a(ssssu)"), data, TRUE);
g_once_init_leave (&geonames_data, v);
}
}
static void
task_func (GTask *task,
gpointer source_object,
gpointer task_data,
GCancellable *cancellable)
{
gchar *query = task_data;
GArray *indices;
indices = geonames_query_cities_db (geonames_data, query);
g_task_return_pointer (task, indices, (GDestroyNotify) g_array_unref);
}
/**
* geonames_query_cities:
* @query: the search string
* @flags: #GeonamesQueryFlags
* @cancellable: (nullable): a #GCancellable
* @callback: (nullable): a #GAsyncReadyCallback
* @user_data: user data passed into @callback
*
* Asynchronously queries the geonames city database with the search
* terms in @query. When the operation is finished, @callback is called
* from the thread-default main context you are calling this method
* from. Call geonames_query_cities_finish() from @callback to retrieve
* the list of results.
*
* Results are weighted by how well and how many tokens match a
* particular city, as well as importance of a city.
*
* If @query is empty, no results are returned.
*/
void
geonames_query_cities (const gchar *query,
GeonamesQueryFlags flags,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GTask *task;
ensure_geonames_data ();
task = g_task_new (NULL, cancellable, callback, user_data);
g_task_set_task_data (task, g_strdup (query), g_free);
g_task_run_in_thread (task, task_func);
}
static gint *
free_index_array (GArray *array,
guint *length)
{
const gint sentinel = -1;
if (length)
*length = array->len;
/* append sentinel after taking the length */
g_array_append_val (array, sentinel);
return (gint *) g_array_free (array, FALSE);
}
/**
* geonames_query_cities_finish:
* @result: the #GAsyncResult from the callback passed to
* geonames_query_cities()
* @length: (out) (optional): optional location for storing the number
* of returned cities
* @error: a #GError
*
* Finishes an operation started with geonames_query_cities() and
* returns the resulting matches.
*
* Returns: (array length=@length): The list of cities matching the
* search query, as indices that can be passed into cities with
* geonames_get_city().
*/
gint *
geonames_query_cities_finish (GAsyncResult *result,
guint *length,
GError **error)
{
GArray *array;
g_return_val_if_fail (g_task_is_valid (result, NULL), NULL);
array = g_task_propagate_pointer (G_TASK (result), error);
if (array == NULL)
return NULL;
return free_index_array (array, length);
}
/**
* geonames_query_cities_sync:
* @query: the search string
* @flags: #GeonamesQueryFlags
* @cancellable: (nullable): a #GCancellable
* @length: (out) (optional): optional location for storing the number
* of returned cities
* @error: a #GError
*
* Synchronous version of geonames_query_cities().
*
* Returns: (array length=@length): The list of cities matching the
* search query, as indices that can be passed into cities with
* geonames_get_city().
*/
gint *
geonames_query_cities_sync (const gchar *query,
GeonamesQueryFlags flags,
guint *length,
GCancellable *cancellable,
GError **error)
{
GArray *indices;
ensure_geonames_data ();
indices = geonames_query_cities_db (geonames_data, query);
return free_index_array (indices, length);
}
/**
* geonames_get_n_cities:
*
* Returns the amount of cities in the geonames database.
*
* Returns: The amount of cities
*/
gint
geonames_get_n_cities (void)
{
ensure_geonames_data ();
return g_variant_n_children (geonames_data);
}
/**
* geonames_get_city:
* @index: The index of the city to retrieve
*
* Retrieves the city at @index in the geonames database.
*
* Returns: (transfer full): the #GeonamesCity at @index in the database
*/
GeonamesCity *
geonames_get_city (gint index)
{
ensure_geonames_data ();
g_return_val_if_fail (index < g_variant_n_children (geonames_data), NULL);
return g_variant_get_child_value (geonames_data, index);
}
/**
* geonames_city_free:
* @city: a #GeonamesCity
*
* Frees @city.
*/
void
geonames_city_free (GeonamesCity *city)
{
g_variant_unref (city);
}
/**
* geonames_city_get_name:
* @city: a #GeonamesCity
*
* Returns: the name of @city
*/
const gchar *
geonames_city_get_name (GeonamesCity *city)
{
const gchar *name;
g_variant_get_child (city, 0, "&s", &name);
return name;
}
/**
* geonames_city_get_state:
* @city: a #GeonamesCity
*
* Returns: the state of @city
*/
const gchar *
geonames_city_get_state (GeonamesCity *city)
{
const gchar *state;
g_variant_get_child (city, 1, "&s", &state);
return state;
}
/**
* geonames_city_get_country:
* @city: a #GeonamesCity
*
* Returns: the country of @city
*/
const gchar *
geonames_city_get_country (GeonamesCity *city)
{
const gchar *country;
g_variant_get_child (city, 2, "&s", &country);
return country;
}
/**
* geonames_city_get_timezone:
* @city: a #GeonamesCity
*
* Returns: the timezone of @city
*/
const gchar *
geonames_city_get_timezone (GeonamesCity *city)
{
const gchar *timezone;
g_variant_get_child (city, 3, "&s", &timezone);
return timezone;
}