summaryrefslogtreecommitdiff
path: root/libmateweather/mateweather-location.c
diff options
context:
space:
mode:
Diffstat (limited to 'libmateweather/mateweather-location.c')
-rw-r--r--libmateweather/mateweather-location.c814
1 files changed, 814 insertions, 0 deletions
diff --git a/libmateweather/mateweather-location.c b/libmateweather/mateweather-location.c
new file mode 100644
index 0000000..df205d4
--- /dev/null
+++ b/libmateweather/mateweather-location.c
@@ -0,0 +1,814 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* mateweather-location.c - Location-handling code
+ *
+ * Copyright 2008, Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <math.h>
+#include <locale.h>
+#include <gtk/gtk.h>
+#include <libxml/xmlreader.h>
+
+#define MATEWEATHER_I_KNOW_THIS_IS_UNSTABLE
+#include "mateweather-location.h"
+#include "mateweather-timezone.h"
+#include "parser.h"
+#include "weather-priv.h"
+
+/**
+ * MateWeatherLocation:
+ *
+ * A #MateWeatherLocation represents a "location" of some type known to
+ * libmateweather; anything from a single weather station to the entire
+ * world. See #MateWeatherLocationLevel for information about how the
+ * hierarchy of locations works.
+ **/
+
+struct _MateWeatherLocation {
+ char *name, *sort_name;
+ MateWeatherLocation *parent, **children;
+ MateWeatherLocationLevel level;
+ char *country_code, *tz_hint;
+ char *station_code, *forecast_zone, *radar;
+ double latitude, longitude;
+ gboolean latlon_valid;
+ MateWeatherTimezone **zones;
+
+ int ref_count;
+};
+
+/**
+ * MateWeatherLocationLevel:
+ * @MATEWEATHER_LOCATION_WORLD: A location representing the entire world.
+ * @MATEWEATHER_LOCATION_REGION: A location representing a continent or
+ * other top-level region.
+ * @MATEWEATHER_LOCATION_COUNTRY: A location representing a "country" (or
+ * other geographic unit that has an ISO-3166 country code)
+ * @MATEWEATHER_LOCATION_ADM1: A location representing a "first-level
+ * administrative division"; ie, a state, province, or similar
+ * division.
+ * @MATEWEATHER_LOCATION_ADM2: A location representing a subdivision of a
+ * %MATEWEATHER_LOCATION_ADM1 location. (Not currently used.)
+ * @MATEWEATHER_LOCATION_CITY: A location representing a city
+ * @MATEWEATHER_LOCATION_WEATHER_STATION: A location representing a
+ * weather station.
+ *
+ * The size/scope of a particular #MateWeatherLocation.
+ *
+ * Locations form a hierarchy, with a %MATEWEATHER_LOCATION_WORLD
+ * location at the top, divided into regions or countries, and so on.
+ * Countries may or may not be divided into "adm1"s, and "adm1"s may
+ * or may not be divided into "adm2"s. A city will have at least one,
+ * and possibly several, weather stations inside it. Weather stations
+ * will never appear outside of cities.
+ **/
+
+static int
+sort_locations_by_name (gconstpointer a, gconstpointer b)
+{
+ MateWeatherLocation *loc_a = *(MateWeatherLocation **)a;
+ MateWeatherLocation *loc_b = *(MateWeatherLocation **)b;
+
+ return g_utf8_collate (loc_a->sort_name, loc_b->sort_name);
+}
+
+static int
+sort_locations_by_distance (gconstpointer a, gconstpointer b, gpointer user_data)
+{
+ MateWeatherLocation *loc_a = *(MateWeatherLocation **)a;
+ MateWeatherLocation *loc_b = *(MateWeatherLocation **)b;
+ MateWeatherLocation *city = (MateWeatherLocation *)user_data;
+ double dist_a, dist_b;
+
+ dist_a = mateweather_location_get_distance (loc_a, city);
+ dist_b = mateweather_location_get_distance (loc_b, city);
+ if (dist_a < dist_b)
+ return -1;
+ else if (dist_a > dist_b)
+ return 1;
+ else
+ return 0;
+}
+
+static gboolean
+parse_coordinates (const char *coordinates,
+ double *latitude, double *longitude)
+{
+ char *p;
+
+ *latitude = g_ascii_strtod (coordinates, &p) * M_PI / 180.0;
+ if (p == (char *)coordinates)
+ return FALSE;
+ if (*p++ != ' ')
+ return FALSE;
+ *longitude = g_ascii_strtod (p, &p) * M_PI / 180.0;
+ return !*p;
+}
+
+static char *
+unparse_coordinates (double latitude, double longitude)
+{
+ int lat_d, lat_m, lat_s, lon_d, lon_m, lon_s;
+ char lat_dir, lon_dir;
+
+ latitude = latitude * 180.0 / M_PI;
+ longitude = longitude * 180.0 / M_PI;
+
+ if (latitude < 0.0) {
+ lat_dir = 'S';
+ latitude = -latitude;
+ } else
+ lat_dir = 'N';
+ if (longitude < 0.0) {
+ lon_dir = 'W';
+ longitude = -longitude;
+ } else
+ lon_dir = 'E';
+
+ lat_d = (int)latitude;
+ lat_m = (int)(latitude * 60.0) - lat_d * 60;
+ lat_s = (int)(latitude * 3600.0) - lat_d * 3600 - lat_m * 60;
+ lon_d = (int)longitude;
+ lon_m = (int)(longitude * 60.0) - lon_d * 60;
+ lon_s = (int)(longitude * 3600.0) - lon_d * 3600 - lon_m * 60;
+
+ return g_strdup_printf ("%02d-%02d-%02d%c %03d-%02d-%02d%c",
+ lat_d, lat_m, lat_s, lat_dir,
+ lon_d, lon_m, lon_s, lon_dir);
+}
+
+static MateWeatherLocation *
+location_new_from_xml (MateWeatherParser *parser, MateWeatherLocationLevel level,
+ MateWeatherLocation *parent)
+{
+ MateWeatherLocation *loc, *child;
+ GPtrArray *children = NULL;
+ const char *tagname;
+ char *value, *normalized;
+ int tagtype, i;
+
+ loc = g_slice_new0 (MateWeatherLocation);
+ loc->parent = parent;
+ loc->level = level;
+ loc->ref_count = 1;
+ children = g_ptr_array_new ();
+
+ if (xmlTextReaderRead (parser->xml) != 1)
+ goto error_out;
+ while ((tagtype = xmlTextReaderNodeType (parser->xml)) !=
+ XML_READER_TYPE_END_ELEMENT) {
+ if (tagtype != XML_READER_TYPE_ELEMENT) {
+ if (xmlTextReaderRead (parser->xml) != 1)
+ goto error_out;
+ continue;
+ }
+
+ tagname = (const char *) xmlTextReaderConstName (parser->xml);
+ if (!strcmp (tagname, "name") && !loc->name) {
+ value = mateweather_parser_get_localized_value (parser);
+ if (!value)
+ goto error_out;
+ loc->name = g_strdup (value);
+ xmlFree (value);
+ normalized = g_utf8_normalize (loc->name, -1, G_NORMALIZE_ALL);
+ loc->sort_name = g_utf8_casefold (normalized, -1);
+ g_free (normalized);
+
+ } else if (!strcmp (tagname, "iso-code") && !loc->country_code) {
+ value = mateweather_parser_get_value (parser);
+ if (!value)
+ goto error_out;
+ loc->country_code = g_strdup (value);
+ xmlFree (value);
+ } else if (!strcmp (tagname, "tz-hint") && !loc->tz_hint) {
+ value = mateweather_parser_get_value (parser);
+ if (!value)
+ goto error_out;
+ loc->tz_hint = g_strdup (value);
+ xmlFree (value);
+ } else if (!strcmp (tagname, "code") && !loc->station_code) {
+ value = mateweather_parser_get_value (parser);
+ if (!value)
+ goto error_out;
+ loc->station_code = g_strdup (value);
+ xmlFree (value);
+ } else if (!strcmp (tagname, "coordinates") && !loc->latlon_valid) {
+ value = mateweather_parser_get_value (parser);
+ if (!value)
+ goto error_out;
+ if (parse_coordinates (value, &loc->latitude, &loc->longitude))
+ loc->latlon_valid = TRUE;
+ xmlFree (value);
+ } else if (!strcmp (tagname, "zone") && !loc->forecast_zone) {
+ value = mateweather_parser_get_value (parser);
+ if (!value)
+ goto error_out;
+ loc->forecast_zone = g_strdup (value);
+ xmlFree (value);
+ } else if (!strcmp (tagname, "radar") && !loc->radar) {
+ value = mateweather_parser_get_value (parser);
+ if (!value)
+ goto error_out;
+ loc->radar = g_strdup (value);
+ xmlFree (value);
+
+ } else if (!strcmp (tagname, "region")) {
+ child = location_new_from_xml (parser, MATEWEATHER_LOCATION_REGION, loc);
+ if (!child)
+ goto error_out;
+ if (parser->use_regions)
+ g_ptr_array_add (children, child);
+ else {
+ if (child->children) {
+ for (i = 0; child->children[i]; i++)
+ g_ptr_array_add (children, mateweather_location_ref (child->children[i]));
+ }
+ mateweather_location_unref (child);
+ }
+ } else if (!strcmp (tagname, "country")) {
+ child = location_new_from_xml (parser, MATEWEATHER_LOCATION_COUNTRY, loc);
+ if (!child)
+ goto error_out;
+ g_ptr_array_add (children, child);
+ } else if (!strcmp (tagname, "state")) {
+ child = location_new_from_xml (parser, MATEWEATHER_LOCATION_ADM1, loc);
+ if (!child)
+ goto error_out;
+ g_ptr_array_add (children, child);
+ } else if (!strcmp (tagname, "city")) {
+ child = location_new_from_xml (parser, MATEWEATHER_LOCATION_CITY, loc);
+ if (!child)
+ goto error_out;
+ g_ptr_array_add (children, child);
+ } else if (!strcmp (tagname, "location")) {
+ child = location_new_from_xml (parser, MATEWEATHER_LOCATION_WEATHER_STATION, loc);
+ if (!child)
+ goto error_out;
+ g_ptr_array_add (children, child);
+
+ } else if (!strcmp (tagname, "timezones")) {
+ loc->zones = mateweather_timezones_parse_xml (parser);
+ if (!loc->zones)
+ goto error_out;
+
+ } else {
+ if (xmlTextReaderNext (parser->xml) != 1)
+ goto error_out;
+ }
+ }
+ if (xmlTextReaderRead (parser->xml) != 1 && parent)
+ goto error_out;
+
+ if (children->len) {
+ if (level == MATEWEATHER_LOCATION_CITY)
+ g_ptr_array_sort_with_data (children, sort_locations_by_distance, loc);
+ else
+ g_ptr_array_sort (children, sort_locations_by_name);
+
+ g_ptr_array_add (children, NULL);
+ loc->children = (MateWeatherLocation **)g_ptr_array_free (children, FALSE);
+ } else
+ g_ptr_array_free (children, TRUE);
+
+ return loc;
+
+error_out:
+ mateweather_location_unref (loc);
+ for (i = 0; i < children->len; i++)
+ mateweather_location_unref (children->pdata[i]);
+ g_ptr_array_free (children, TRUE);
+
+ return NULL;
+}
+
+/**
+ * mateweather_location_new_world:
+ * @use_regions: whether or not to divide the world into regions
+ *
+ * Creates a new #MateWeatherLocation of type %MATEWEATHER_LOCATION_WORLD,
+ * representing a hierarchy containing all of the locations from
+ * Locations.xml.
+ *
+ * If @use_regions is %TRUE, the immediate children of the returned
+ * location will be %MATEWEATHER_LOCATION_REGION nodes, representing the
+ * top-level "regions" of Locations.xml (the continents and a few
+ * other divisions), and the country-level nodes will be the children
+ * of the regions. If @use_regions is %FALSE, the regions will be
+ * skipped, and the children of the returned location will be the
+ * %MATEWEATHER_LOCATION_COUNTRY nodes.
+ *
+ * Return value: (allow-none): a %MATEWEATHER_LOCATION_WORLD location, or
+ * %NULL if Locations.xml could not be found or could not be parsed.
+ **/
+MateWeatherLocation *
+mateweather_location_new_world (gboolean use_regions)
+{
+ MateWeatherParser *parser;
+ MateWeatherLocation *world;
+
+ parser = mateweather_parser_new (use_regions);
+ if (!parser)
+ return NULL;
+
+ world = location_new_from_xml (parser, MATEWEATHER_LOCATION_WORLD, NULL);
+
+ mateweather_parser_free (parser);
+ return world;
+}
+
+/**
+ * mateweather_location_ref:
+ * @loc: a #MateWeatherLocation
+ *
+ * Adds 1 to @loc's reference count.
+ *
+ * Return value: @loc
+ **/
+MateWeatherLocation *
+mateweather_location_ref (MateWeatherLocation *loc)
+{
+ g_return_val_if_fail (loc != NULL, NULL);
+
+ loc->ref_count++;
+ return loc;
+}
+
+/**
+ * mateweather_location_unref:
+ * @loc: a #MateWeatherLocation
+ *
+ * Subtracts 1 from @loc's reference count, and frees it if the
+ * reference count reaches 0.
+ **/
+void
+mateweather_location_unref (MateWeatherLocation *loc)
+{
+ int i;
+
+ g_return_if_fail (loc != NULL);
+
+ if (--loc->ref_count)
+ return;
+
+ g_free (loc->name);
+ g_free (loc->sort_name);
+ g_free (loc->country_code);
+ g_free (loc->tz_hint);
+ g_free (loc->station_code);
+ g_free (loc->forecast_zone);
+ g_free (loc->radar);
+
+ if (loc->children) {
+ for (i = 0; loc->children[i]; i++) {
+ loc->children[i]->parent = NULL;
+ mateweather_location_unref (loc->children[i]);
+ }
+ g_free (loc->children);
+ }
+
+ if (loc->zones) {
+ for (i = 0; loc->zones[i]; i++)
+ mateweather_timezone_unref (loc->zones[i]);
+ g_free (loc->zones);
+ }
+
+ g_slice_free (MateWeatherLocation, loc);
+}
+
+GType
+mateweather_location_get_type (void)
+{
+ static volatile gsize type_volatile = 0;
+
+ if (g_once_init_enter (&type_volatile)) {
+ GType type = g_boxed_type_register_static (
+ g_intern_static_string ("MateWeatherLocation"),
+ (GBoxedCopyFunc) mateweather_location_ref,
+ (GBoxedFreeFunc) mateweather_location_unref);
+ g_once_init_leave (&type_volatile, type);
+ }
+ return type_volatile;
+}
+
+/**
+ * mateweather_location_get_name:
+ * @loc: a #MateWeatherLocation
+ *
+ * Gets @loc's name, localized into the current language.
+ *
+ * Note that %MATEWEATHER_LOCATION_WEATHER_STATION nodes are not
+ * localized, and so the name returned for those nodes will always be
+ * in English, and should therefore not be displayed to the user.
+ * (FIXME: should we just not return a name?)
+ *
+ * Return value: @loc's name
+ **/
+const char *
+mateweather_location_get_name (MateWeatherLocation *loc)
+{
+ g_return_val_if_fail (loc != NULL, NULL);
+ return loc->name;
+}
+
+/**
+ * mateweather_location_get_sort_name:
+ * @loc: a #MateWeatherLocation
+ *
+ * Gets @loc's "sort name", which is the name after having
+ * g_utf8_normalize() (with %G_NORMALIZE_ALL) and g_utf8_casefold()
+ * called on it. You can use this to sort locations, or to comparing
+ * user input against a location name.
+ *
+ * Return value: @loc's sort name
+ **/
+const char *
+mateweather_location_get_sort_name (MateWeatherLocation *loc)
+{
+ g_return_val_if_fail (loc != NULL, NULL);
+ return loc->sort_name;
+}
+
+/**
+ * mateweather_location_get_level:
+ * @loc: a #MateWeatherLocation
+ *
+ * Gets @loc's level, from %MATEWEATHER_LOCATION_WORLD, to
+ * %MATEWEATHER_LOCATION_WEATHER_STATION.
+ *
+ * Return value: @loc's level
+ **/
+MateWeatherLocationLevel
+mateweather_location_get_level (MateWeatherLocation *loc)
+{
+ g_return_val_if_fail (loc != NULL, MATEWEATHER_LOCATION_WORLD);
+ return loc->level;
+}
+
+/**
+ * mateweather_location_get_parent:
+ * @loc: a #MateWeatherLocation
+ *
+ * Gets @loc's parent location.
+ *
+ * Return value: (transfer none) (allow-none): @loc's parent, or %NULL
+ * if @loc is a %MATEWEATHER_LOCATION_WORLD node.
+ **/
+MateWeatherLocation *
+mateweather_location_get_parent (MateWeatherLocation *loc)
+{
+ g_return_val_if_fail (loc != NULL, NULL);
+ return loc->parent;
+}
+
+/**
+ * mateweather_location_get_children:
+ * @loc: a #MateWeatherLocation
+ *
+ * Gets an array of @loc's children; this is owned by @loc and will
+ * not remain valid if @loc is freed.
+ *
+ * Return value: (transfer none) (array zero-terminated=1): @loc's
+ * children. (May be empty, but will not be %NULL.)
+ **/
+MateWeatherLocation **
+mateweather_location_get_children (MateWeatherLocation *loc)
+{
+ static MateWeatherLocation *no_children = NULL;
+
+ g_return_val_if_fail (loc != NULL, NULL);
+
+ if (loc->children)
+ return loc->children;
+ else
+ return &no_children;
+}
+
+
+/**
+ * mateweather_location_free_children:
+ * @loc: a #MateWeatherLocation
+ * @children: an array of @loc's children
+ *
+ * This is a no-op. Do not use it.
+ *
+ * Deprecated: This is a no-op.
+ **/
+void
+mateweather_location_free_children (MateWeatherLocation *loc,
+ MateWeatherLocation **children)
+{
+ ;
+}
+
+/**
+ * mateweather_location_has_coords:
+ * @loc: a #MateWeatherLocation
+ *
+ * Checks if @loc has valid latitude and longitude.
+ *
+ * Return value: %TRUE if @loc has valid latitude and longitude.
+ **/
+gboolean
+mateweather_location_has_coords (MateWeatherLocation *loc)
+{
+ g_return_val_if_fail (loc != NULL, FALSE);
+ return loc->latlon_valid;
+}
+
+/**
+ * mateweather_location_get_coords:
+ * @loc: a #MateWeatherLocation
+ * @latitude: (out): on return will contain @loc's latitude
+ * @longitude: (out): on return will contain @loc's longitude
+ *
+ * Gets @loc's coordinates; you must check
+ * mateweather_location_has_coords() before calling this.
+ **/
+void
+mateweather_location_get_coords (MateWeatherLocation *loc,
+ double *latitude, double *longitude)
+{
+ //g_return_if_fail (loc->latlon_valid);
+ g_return_if_fail (loc != NULL);
+ g_return_if_fail (latitude != NULL);
+ g_return_if_fail (longitude != NULL);
+
+ *latitude = loc->latitude / M_PI * 180.0;
+ *longitude = loc->longitude / M_PI * 180.0;
+}
+
+/**
+ * mateweather_location_get_distance:
+ * @loc: a #MateWeatherLocation
+ * @loc2: a second #MateWeatherLocation
+ *
+ * Determines the distance in kilometers between @loc and @loc2.
+ *
+ * Return value: the distance between @loc and @loc2.
+ **/
+double
+mateweather_location_get_distance (MateWeatherLocation *loc, MateWeatherLocation *loc2)
+{
+ /* average radius of the earth in km */
+ static const double radius = 6372.795;
+
+ g_return_val_if_fail (loc != NULL, 0);
+ g_return_val_if_fail (loc2 != NULL, 0);
+
+ //g_return_val_if_fail (loc->latlon_valid, 0.0);
+ //g_return_val_if_fail (loc2->latlon_valid, 0.0);
+
+ return acos (cos (loc->latitude) * cos (loc2->latitude) * cos (loc->longitude - loc2->longitude) +
+ sin (loc->latitude) * sin (loc2->latitude)) * radius;
+}
+
+/**
+ * mateweather_location_get_country:
+ * @loc: a #MateWeatherLocation
+ *
+ * Gets the ISO 3166 country code of @loc (or %NULL if @loc is a
+ * region- or world-level location)
+ *
+ * Return value: (allow-none): @loc's country code (or %NULL if @loc
+ * is a region- or world-level location)
+ **/
+const char *
+mateweather_location_get_country (MateWeatherLocation *loc)
+{
+ g_return_val_if_fail (loc != NULL, NULL);
+
+ while (loc->parent && !loc->country_code)
+ loc = loc->parent;
+ return loc->country_code;
+}
+
+/**
+ * mateweather_location_get_timezone:
+ * @loc: a #MateWeatherLocation
+ *
+ * Gets the timezone associated with @loc, if known.
+ *
+ * The timezone is owned either by @loc or by one of its parents.
+ * FIXME.
+ *
+ * Return value: (transfer none) (allow-none): @loc's timezone, or
+ * %NULL
+ **/
+MateWeatherTimezone *
+mateweather_location_get_timezone (MateWeatherLocation *loc)
+{
+ const char *tz_hint;
+ int i;
+
+ g_return_val_if_fail (loc != NULL, NULL);
+
+ while (loc && !loc->tz_hint)
+ loc = loc->parent;
+ if (!loc)
+ return NULL;
+ tz_hint = loc->tz_hint;
+
+ while (loc) {
+ while (loc && !loc->zones)
+ loc = loc->parent;
+ if (!loc)
+ return NULL;
+ for (i = 0; loc->zones[i]; i++) {
+ if (!strcmp (tz_hint, mateweather_timezone_get_tzid (loc->zones[i])))
+ return loc->zones[i];
+ }
+ loc = loc->parent;
+ }
+
+ return NULL;
+}
+
+static void
+add_timezones (MateWeatherLocation *loc, GPtrArray *zones)
+{
+ int i;
+
+ if (loc->zones) {
+ for (i = 0; loc->zones[i]; i++)
+ g_ptr_array_add (zones, mateweather_timezone_ref (loc->zones[i]));
+ }
+ if (loc->level < MATEWEATHER_LOCATION_COUNTRY && loc->children) {
+ for (i = 0; loc->children[i]; i++)
+ add_timezones (loc->children[i], zones);
+ }
+}
+
+/**
+ * mateweather_location_get_timezones:
+ * @loc: a #MateWeatherLocation
+ *
+ * Gets an array of all timezones associated with any location under
+ * @loc. You can use mateweather_location_free_timezones() to free this
+ * array.
+ *
+ * Return value: (transfer full) (array zero-terminated=1): an array
+ * of timezones. May be empty but will not be %NULL.
+ **/
+MateWeatherTimezone **
+mateweather_location_get_timezones (MateWeatherLocation *loc)
+{
+ GPtrArray *zones;
+
+ g_return_val_if_fail (loc != NULL, NULL);
+
+ zones = g_ptr_array_new ();
+ add_timezones (loc, zones);
+ g_ptr_array_add (zones, NULL);
+ return (MateWeatherTimezone **)g_ptr_array_free (zones, FALSE);
+}
+
+/**
+ * mateweather_location_free_timezones:
+ * @loc: a #MateWeatherLocation
+ * @zones: an array returned from mateweather_location_get_timezones()
+ *
+ * Frees the array of timezones returned by
+ * mateweather_location_get_timezones().
+ **/
+void
+mateweather_location_free_timezones (MateWeatherLocation *loc,
+ MateWeatherTimezone **zones)
+{
+ int i;
+
+ g_return_if_fail (loc != NULL);
+ g_return_if_fail (zones != NULL);
+
+ for (i = 0; zones[i]; i++)
+ mateweather_timezone_unref (zones[i]);
+ g_free (zones);
+}
+
+/**
+ * mateweather_location_get_code:
+ * @loc: a #MateWeatherLocation
+ *
+ * Gets the METAR station code associated with a
+ * %MATEWEATHER_LOCATION_WEATHER_STATION location.
+ *
+ * Return value: (allow-none): @loc's METAR station code, or %NULL
+ **/
+const char *
+mateweather_location_get_code (MateWeatherLocation *loc)
+{
+ g_return_val_if_fail (loc != NULL, NULL);
+ return loc->station_code;
+}
+
+/**
+ * mateweather_location_get_city_name:
+ * @loc: a #MateWeatherLocation
+ *
+ * For a %MATEWEATHER_LOCATION_CITY location, this is equivalent to
+ * mateweather_location_get_name(). For a
+ * %MATEWEATHER_LOCATION_WEATHER_STATION location, it is equivalent to
+ * calling mateweather_location_get_name() on the location's parent. For
+ * other locations it will return %NULL.
+ *
+ * Return value: (allow-none) @loc's city name, or %NULL
+ **/
+char *
+mateweather_location_get_city_name (MateWeatherLocation *loc)
+{
+ g_return_val_if_fail (loc != NULL, NULL);
+
+ if (loc->level == MATEWEATHER_LOCATION_CITY)
+ return g_strdup (loc->name);
+ else if (loc->level == MATEWEATHER_LOCATION_WEATHER_STATION &&
+ loc->parent &&
+ loc->parent->level == MATEWEATHER_LOCATION_CITY)
+ return g_strdup (loc->parent->name);
+ else
+ return NULL;
+}
+
+WeatherLocation *
+mateweather_location_to_weather_location (MateWeatherLocation *gloc,
+ const char *name)
+{
+ const char *code = NULL, *zone = NULL, *radar = NULL, *tz_hint = NULL;
+ MateWeatherLocation *l;
+ WeatherLocation *wloc;
+ char *coords;
+
+ g_return_val_if_fail (gloc != NULL, NULL);
+
+ if (!name)
+ name = mateweather_location_get_name (gloc);
+
+ if (gloc->level == MATEWEATHER_LOCATION_CITY && gloc->children)
+ l = gloc->children[0];
+ else
+ l = gloc;
+
+ if (l->latlon_valid)
+ coords = unparse_coordinates (l->latitude, l->longitude);
+ else
+ coords = NULL;
+
+ while (l && (!code || !zone || !radar || !tz_hint)) {
+ if (!code && l->station_code)
+ code = l->station_code;
+ if (!zone && l->forecast_zone)
+ zone = l->forecast_zone;
+ if (!radar && l->radar)
+ radar = l->radar;
+ if (!tz_hint && l->tz_hint)
+ tz_hint = l->tz_hint;
+ l = l->parent;
+ }
+
+ wloc = weather_location_new (name, code, zone, radar, coords,
+ mateweather_location_get_country (gloc),
+ tz_hint);
+ g_free (coords);
+ return wloc;
+}
+
+/**
+ * mateweather_location_get_weather:
+ * @loc: a %MateWeatherLocation
+ *
+ * Creates a #WeatherInfo corresponding to @loc; you can use
+ * weather_info_update() to fill it in.
+ *
+ * Return value: (transfer full): a #WeatherInfo corresponding to
+ * @loc.
+ **/
+WeatherInfo *
+mateweather_location_get_weather (MateWeatherLocation *loc)
+{
+ WeatherLocation *wloc;
+ WeatherInfo *info;
+
+ g_return_val_if_fail (loc != NULL, NULL);
+
+ wloc = mateweather_location_to_weather_location (loc, NULL);
+ info = weather_info_new (wloc, NULL, NULL, NULL);
+ weather_location_free (wloc);
+ return info;
+}