[libgweather/wip/forecasts: 1/6] Add Yahoo! Weather forecast provider.
- From: Giovanni Campagna <gcampagna src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [libgweather/wip/forecasts: 1/6] Add Yahoo! Weather forecast provider.
- Date: Sat, 1 Dec 2012 15:09:36 +0000 (UTC)
commit 349f453c9ac8c8b464b631b6c53e1de30932bc77
Author: Giovanni Campagna <gcampagna src gnome org>
Date: Sun Mar 18 19:04:08 2012 +0100
Add Yahoo! Weather forecast provider.
Adds a forecast provider that uses Yahoo! Weather RSS channels to
retrieve informations. For the time being, this requires a new tag
in the database, <yahoo-woeid>, containing a Where on Earth ID (the
one for Milan is provided for testing purposes).
The matchings from Yahoo condition codes to GWeatherConditions and
GWeatherSky need to be checked.
data/Locations.xml.in | 1 +
data/locations.dtd | 5 +-
libgweather/Makefile.am | 2 +-
libgweather/gweather-location.c | 14 ++-
libgweather/weather-iwin.c | 35 +++--
libgweather/weather-priv.h | 8 +-
libgweather/weather-yahoo.c | 299 +++++++++++++++++++++++++++++++++++++++
libgweather/weather.c | 18 ++-
8 files changed, 357 insertions(+), 25 deletions(-)
---
diff --git a/data/Locations.xml.in b/data/Locations.xml.in
index 3182161..8cc7926 100644
--- a/data/Locations.xml.in
+++ b/data/Locations.xml.in
@@ -16058,6 +16058,7 @@
<location>
<name>Linate Airport</name>
<code>LIML</code>
+ <yahoo-woeid>718345</yahoo-woeid>
<coordinates>45.433333 9.283333</coordinates>
</location>
<location>
diff --git a/data/locations.dtd b/data/locations.dtd
index 33afd95..f30376e 100644
--- a/data/locations.dtd
+++ b/data/locations.dtd
@@ -7,8 +7,8 @@
<!ELEMENT region (%name;, country+) >
<!ELEMENT country (%name;, iso-code, fips-code+, pref-lang?, timezones, tz-hint?, (location|state|city)*) >
<!ELEMENT state (%name;, fips-code+, tz-hint?, (location|city)*) >
-<!ELEMENT city (%name;, coordinates?, zone?, radar?, location+) >
-<!ELEMENT location (%name;, code, tz-hint?, zone?, radar?, coordinates?) >
+<!ELEMENT city (%name;, coordinates?, location+) >
+<!ELEMENT location (%name;, code, tz-hint?, zone?, yahoo-woeid?, radar?, coordinates?) >
<!ELEMENT timezones (timezone+) >
<!ELEMENT timezone (_name?, name*, obsoletes*) >
@@ -31,3 +31,4 @@
<!ELEMENT zone (#PCDATA) >
<!ELEMENT radar (#PCDATA) >
<!ELEMENT coordinates (#PCDATA) >
+<!ELEMENT yahoo-woeid (#PCDATA) >
diff --git a/libgweather/Makefile.am b/libgweather/Makefile.am
index a5e514b..f78891b 100644
--- a/libgweather/Makefile.am
+++ b/libgweather/Makefile.am
@@ -29,7 +29,7 @@ noinst_HEADERS = weather-priv.h gweather-win32.h
libgweather_internal_3_la_SOURCES = \
weather.c weather-priv.h \
weather-metar.c weather-iwin.c weather-met.c \
- weather-bom.c weather-wx.c \
+ weather-bom.c weather-yahoo.c weather-wx.c \
weather-sun.c weather-moon.c \
gweather-enum-types.c \
gweather-xml.c gweather-xml.h \
diff --git a/libgweather/gweather-location.c b/libgweather/gweather-location.c
index b1d12e1..8469c70 100644
--- a/libgweather/gweather-location.c
+++ b/libgweather/gweather-location.c
@@ -49,7 +49,7 @@ struct _GWeatherLocation {
GWeatherLocation *parent, **children;
GWeatherLocationLevel level;
char *country_code, *tz_hint;
- char *station_code, *forecast_zone, *radar;
+ char *station_code, *forecast_zone, *yahoo_id, *radar;
double latitude, longitude;
gboolean latlon_valid;
GWeatherTimezone **zones;
@@ -197,6 +197,12 @@ location_new_from_xml (GWeatherParser *parser, GWeatherLocationLevel level,
goto error_out;
loc->forecast_zone = g_strdup (value);
xmlFree (value);
+ } else if (!strcmp (tagname, "yahoo-woeid") && !loc->yahoo_id) {
+ value = gweather_parser_get_value (parser);
+ if (!value)
+ goto error_out;
+ loc->yahoo_id = g_strdup (value);
+ xmlFree (value);
} else if (!strcmp (tagname, "radar") && !loc->radar) {
value = gweather_parser_get_value (parser);
if (!value)
@@ -730,7 +736,7 @@ gweather_location_get_city_name (GWeatherLocation *loc)
WeatherLocation *
_weather_location_from_gweather_location (GWeatherLocation *gloc, const gchar *name)
{
- const char *code = NULL, *zone = NULL, *radar = NULL, *tz_hint = NULL;
+ const char *code = NULL, *zone = NULL, *yahoo_id = NULL, *radar = NULL, *tz_hint = NULL;
gboolean latlon_valid = FALSE;
gdouble lat = DBL_MAX, lon = DBL_MAX;
GWeatherLocation *l;
@@ -748,6 +754,8 @@ _weather_location_from_gweather_location (GWeatherLocation *gloc, const gchar *n
code = l->station_code;
if (!zone && l->forecast_zone)
zone = l->forecast_zone;
+ if (!yahoo_id && l->yahoo_id)
+ yahoo_id = l->yahoo_id;
if (!radar && l->radar)
radar = l->radar;
if (!tz_hint && l->tz_hint)
@@ -761,7 +769,7 @@ _weather_location_from_gweather_location (GWeatherLocation *gloc, const gchar *n
}
wloc = _weather_location_new (name ? name : gweather_location_get_name (gloc),
- code, zone, radar,
+ code, zone, yahoo_id, radar,
latlon_valid, lat, lon,
gweather_location_get_country (gloc),
tz_hint);
diff --git a/libgweather/weather-iwin.c b/libgweather/weather-iwin.c
index f2baefa..0a446e0 100644
--- a/libgweather/weather-iwin.c
+++ b/libgweather/weather-iwin.c
@@ -382,7 +382,7 @@ iwin_finish (SoupSession *session, SoupMessage *msg, gpointer data)
}
/* Get forecast into newly alloc'ed string */
-void
+gboolean
iwin_start_open (GWeatherInfo *info)
{
GWeatherInfoPrivate *priv;
@@ -390,21 +390,28 @@ iwin_start_open (GWeatherInfo *info)
WeatherLocation *loc;
SoupMessage *msg;
- g_return_if_fail (info != NULL);
+ g_return_val_if_fail (info != NULL, FALSE);
priv = info->priv;
loc = priv->location;
- g_return_if_fail (loc != NULL);
+ g_return_val_if_fail (loc != NULL, FALSE);
- if ((!loc->zone || loc->zone[0] == '-') && (priv->forecast_type != GWEATHER_FORECAST_LIST || !loc->latlon_valid))
- return;
+ /* No zone (or -) means no weather information from national offices */
+ if ((!loc->zone || loc->zone[0] == '-'))
+ return FALSE;
- if (priv->forecast) {
- g_free (priv->forecast);
- priv->forecast = NULL;
- }
+ /* Zones starting with : are for the UK Met Office, @ is for Austrialian
+ Bureau of Metereology. GWEATHER_FORECAST_LIST only works for US, so bail
+ out early if the location is outside.
+ */
+ if (priv->forecast_type == GWEATHER_FORECAST_LIST &&
+ (loc->zone[0] == ':' || loc->zone[0] == '@'))
+ return FALSE;
- free_forecast_list (info);
+ /* We also need a pair of coordinates for GWEATHER_FORECAST_LIST */
+ if (priv->forecast_type == GWEATHER_FORECAST_LIST &&
+ !loc->latlon_valid)
+ return FALSE;
if (priv->forecast_type == GWEATHER_FORECAST_LIST) {
/* see the description here: http://www.weather.gov/forecasts/xml/ */
@@ -424,17 +431,17 @@ iwin_start_open (GWeatherInfo *info)
soup_session_queue_message (priv->session, msg, iwin_finish, info);
priv->requests_pending++;
- return;
+ return TRUE;
}
if (loc->zone[0] == ':') {
/* Met Office Region Names */
metoffice_start_open (info);
- return;
+ return TRUE;
} else if (loc->zone[0] == '@') {
/* Australian BOM forecasts */
bom_start_open (info);
- return;
+ return TRUE;
}
/* The zone for Pittsburgh (for example) is given as PAZ021 in the locations
@@ -454,4 +461,6 @@ iwin_start_open (GWeatherInfo *info)
soup_session_queue_message (priv->session, msg, iwin_finish, info);
priv->requests_pending++;
+
+ return TRUE;
}
diff --git a/libgweather/weather-priv.h b/libgweather/weather-priv.h
index 01782b1..c2320ab 100644
--- a/libgweather/weather-priv.h
+++ b/libgweather/weather-priv.h
@@ -47,13 +47,11 @@ const char *gweather_dpgettext (const char *context, const char *str) G_GNUC_FOR
#define WEATHER_LOCATION_CODE_LEN 4
-/* Why we have this in code?
- (at least, it's not exposed in API anymore)
-*/
typedef struct {
gchar *name;
gchar *code;
gchar *zone;
+ gchar *yahoo_id;
gchar *radar;
gboolean latlon_valid;
gdouble latitude;
@@ -67,6 +65,7 @@ WeatherLocation * _weather_location_from_gweather_location (GWeatherLocati
WeatherLocation * _weather_location_new (const gchar *trans_name,
const gchar *code,
const gchar *zone,
+ const gchar *yahoo_id,
const gchar *radar,
gboolean latlon_valid,
double latitude,
@@ -175,10 +174,11 @@ struct _GWeatherInfoPrivate {
#define PERIGEE_LONGITUDE(d) (282.93768193 + (d)/36525.*0.32327364)
void metar_start_open (GWeatherInfo *info);
-void iwin_start_open (GWeatherInfo *info);
+gboolean iwin_start_open (GWeatherInfo *info);
void metoffice_start_open (GWeatherInfo *info);
void bom_start_open (GWeatherInfo *info);
void wx_start_open (GWeatherInfo *info);
+void yahoo_start_open (GWeatherInfo *info);
gboolean metar_parse (gchar *metar,
GWeatherInfo *info);
diff --git a/libgweather/weather-yahoo.c b/libgweather/weather-yahoo.c
new file mode 100644
index 0000000..b0500f7
--- /dev/null
+++ b/libgweather/weather-yahoo.c
@@ -0,0 +1,299 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* weather-yahoo.c - Yahoo! Weather service.
+ *
+ * Copyright 2012 Giovanni Campagna <scampa giovanni gmail com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program 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
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE /* for strptime */
+#include <time.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include <glib.h>
+
+#include <libxml/parser.h>
+#include <libxml/xpath.h>
+#include <libxml/xpathInternals.h>
+
+#define GWEATHER_I_KNOW_THIS_IS_UNSTABLE
+#include "weather-priv.h"
+
+#define XC(t) ((const xmlChar *)(t))
+
+static GWeatherConditions condition_codes[] = {
+ { TRUE, GWEATHER_PHENOMENON_TORNADO, GWEATHER_QUALIFIER_NONE }, /* tornado */
+ { TRUE, GWEATHER_PHENOMENON_INVALID, GWEATHER_QUALIFIER_NONE }, /* FIXME: tropical storm */
+ { TRUE, GWEATHER_PHENOMENON_INVALID, GWEATHER_QUALIFIER_NONE }, /* FIXME: hurricane */
+ { TRUE, GWEATHER_PHENOMENON_RAIN, GWEATHER_QUALIFIER_THUNDERSTORM }, /* FIXME: severe thunderstorms */
+ { TRUE, GWEATHER_PHENOMENON_RAIN, GWEATHER_QUALIFIER_THUNDERSTORM }, /* thunderstorms */
+ { TRUE, GWEATHER_PHENOMENON_RAIN, GWEATHER_QUALIFIER_NONE }, /* FIXME: mixed rain and snow */
+ { TRUE, GWEATHER_PHENOMENON_RAIN, GWEATHER_QUALIFIER_NONE }, /* FIXME: mixed rain and sleet */
+ { TRUE, GWEATHER_PHENOMENON_SNOW, GWEATHER_QUALIFIER_NONE }, /* FIXME: mixed snow and sleet */
+ { TRUE, GWEATHER_PHENOMENON_DRIZZLE, GWEATHER_QUALIFIER_FREEZING }, /* freezing drizzle */
+ { TRUE, GWEATHER_PHENOMENON_DRIZZLE, GWEATHER_QUALIFIER_NONE }, /* drizzle */
+ { TRUE, GWEATHER_PHENOMENON_RAIN, GWEATHER_QUALIFIER_FREEZING }, /* freezing rain */
+ { TRUE, GWEATHER_PHENOMENON_RAIN, GWEATHER_QUALIFIER_SHOWERS }, /* showers */
+ { TRUE, GWEATHER_PHENOMENON_RAIN, GWEATHER_QUALIFIER_SHOWERS }, /* showers */
+ { TRUE, GWEATHER_PHENOMENON_SNOW, GWEATHER_QUALIFIER_BLOWING }, /* FIXME: snow flurries */
+ { TRUE, GWEATHER_PHENOMENON_SNOW, GWEATHER_QUALIFIER_LIGHT }, /* FIXME: light snow showers */
+ { TRUE, GWEATHER_PHENOMENON_SNOW, GWEATHER_QUALIFIER_BLOWING }, /* blowing snow */
+ { TRUE, GWEATHER_PHENOMENON_SNOW, GWEATHER_QUALIFIER_NONE }, /* snow */
+ { TRUE, GWEATHER_PHENOMENON_HAIL, GWEATHER_QUALIFIER_NONE }, /* hail */
+ { TRUE, GWEATHER_PHENOMENON_SNOW, GWEATHER_QUALIFIER_NONE }, /* FIXME: sleet */
+ { TRUE, GWEATHER_PHENOMENON_DUST, GWEATHER_QUALIFIER_NONE }, /* dust */
+ { TRUE, GWEATHER_PHENOMENON_FOG, GWEATHER_QUALIFIER_NONE }, /* foggy */
+ { TRUE, GWEATHER_PHENOMENON_HAZE, GWEATHER_QUALIFIER_NONE }, /* haze */
+ { TRUE, GWEATHER_PHENOMENON_SMOKE, GWEATHER_QUALIFIER_NONE }, /* smoky */
+ { TRUE, GWEATHER_PHENOMENON_INVALID, GWEATHER_QUALIFIER_NONE }, /* FIXME: blustery */
+ { TRUE, GWEATHER_PHENOMENON_INVALID, GWEATHER_QUALIFIER_NONE }, /* FIXME: windy */
+ { FALSE, GWEATHER_PHENOMENON_NONE, GWEATHER_QUALIFIER_NONE }, /* cold */
+ { FALSE, GWEATHER_PHENOMENON_NONE, GWEATHER_QUALIFIER_NONE }, /* cloudy */
+ { FALSE, GWEATHER_PHENOMENON_NONE, GWEATHER_QUALIFIER_NONE }, /* mostly cloudy (night) */
+ { FALSE, GWEATHER_PHENOMENON_NONE, GWEATHER_QUALIFIER_NONE }, /* mostly cloudy (day) */
+ { FALSE, GWEATHER_PHENOMENON_NONE, GWEATHER_QUALIFIER_NONE }, /* partly cloudy (night) */
+ { FALSE, GWEATHER_PHENOMENON_NONE, GWEATHER_QUALIFIER_NONE }, /* partly cloudy (day) */
+ { FALSE, GWEATHER_PHENOMENON_NONE, GWEATHER_QUALIFIER_NONE }, /* clear (night) */
+ { FALSE, GWEATHER_PHENOMENON_NONE, GWEATHER_QUALIFIER_NONE }, /* sunny */
+ { FALSE, GWEATHER_PHENOMENON_NONE, GWEATHER_QUALIFIER_NONE }, /* fair (night) */
+ { FALSE, GWEATHER_PHENOMENON_NONE, GWEATHER_QUALIFIER_NONE }, /* fair (day) */
+ { TRUE, GWEATHER_PHENOMENON_HAIL, GWEATHER_QUALIFIER_NONE }, /* FIXME: mixed_rain_and_hail */
+ { FALSE, GWEATHER_PHENOMENON_NONE, GWEATHER_QUALIFIER_NONE }, /* hot */
+ { TRUE, GWEATHER_PHENOMENON_RAIN, GWEATHER_QUALIFIER_THUNDERSTORM }, /* FIXME: isolated thunderstorms */
+ { TRUE, GWEATHER_PHENOMENON_RAIN, GWEATHER_QUALIFIER_THUNDERSTORM }, /* FIXME: scattered thunderstorms */
+ { TRUE, GWEATHER_PHENOMENON_RAIN, GWEATHER_QUALIFIER_THUNDERSTORM }, /* FIXME: scattered thunderstorms */
+ { TRUE, GWEATHER_PHENOMENON_RAIN, GWEATHER_QUALIFIER_SHOWERS }, /* FIXME: scattered showers */
+ { TRUE, GWEATHER_PHENOMENON_SNOW, GWEATHER_QUALIFIER_HEAVY }, /* heavy snow */
+ { TRUE, GWEATHER_PHENOMENON_SNOW, GWEATHER_QUALIFIER_SHOWERS }, /* FIXME: scattered snow showers */
+ { TRUE, GWEATHER_PHENOMENON_SNOW, GWEATHER_QUALIFIER_HEAVY }, /* heavy snow */
+ { FALSE, GWEATHER_PHENOMENON_NONE, GWEATHER_QUALIFIER_NONE }, /* partly cloudy */
+ { TRUE, GWEATHER_PHENOMENON_RAIN, GWEATHER_QUALIFIER_THUNDERSTORM }, /* FIXME: thundershowers */
+ { TRUE, GWEATHER_PHENOMENON_SNOW, GWEATHER_QUALIFIER_SHOWERS }, /* snow showers */
+ { TRUE, GWEATHER_PHENOMENON_RAIN, GWEATHER_QUALIFIER_THUNDERSTORM }, /* FIXME: isolated thundershowers */
+};
+
+/* FIXME: check sky values for codes that have a phenomenon too
+ (scattered is what weather-iwin.c does for rain and snow)
+*/
+static GWeatherSky sky_codes[] = {
+ GWEATHER_SKY_INVALID, /* tornado */
+ GWEATHER_SKY_SCATTERED, /* tropical storm */
+ GWEATHER_SKY_SCATTERED, /* hurricane */
+ GWEATHER_SKY_SCATTERED, /* severe thunderstorms */
+ GWEATHER_SKY_SCATTERED, /* thunderstorms */
+ GWEATHER_SKY_SCATTERED, /* mixed rain and snow */
+ GWEATHER_SKY_SCATTERED, /* mixed rain and sleet */
+ GWEATHER_SKY_SCATTERED, /* mixed snow and sleet */
+ GWEATHER_SKY_SCATTERED, /* freezing drizzle */
+ GWEATHER_SKY_SCATTERED, /* drizzle */
+ GWEATHER_SKY_SCATTERED, /* freezing rain */
+ GWEATHER_SKY_SCATTERED, /* showers */
+ GWEATHER_SKY_SCATTERED, /* showers */
+ GWEATHER_SKY_SCATTERED, /* snow flurries */
+ GWEATHER_SKY_SCATTERED, /* light snow showers */
+ GWEATHER_SKY_SCATTERED, /* blowing snow */
+ GWEATHER_SKY_SCATTERED, /* snow */
+ GWEATHER_SKY_INVALID, /* hail */
+ GWEATHER_SKY_INVALID, /* sleet */
+ GWEATHER_SKY_INVALID, /* dust */
+ GWEATHER_SKY_INVALID, /* foggy */
+ GWEATHER_SKY_INVALID, /* haze */
+ GWEATHER_SKY_INVALID, /* smoky */
+ GWEATHER_SKY_INVALID, /* blustery */
+ GWEATHER_SKY_INVALID, /* windy */
+ GWEATHER_SKY_CLEAR, /* cold */
+ GWEATHER_SKY_OVERCAST, /* cloudy */
+ GWEATHER_SKY_FEW, /* mostly cloudy (night) */
+ GWEATHER_SKY_FEW, /* mostly cloudy (day) */
+ GWEATHER_SKY_BROKEN, /* partly cloudy (night) */
+ GWEATHER_SKY_BROKEN, /* partly cloudy (day) */
+ GWEATHER_SKY_CLEAR, /* clear (night) */
+ GWEATHER_SKY_CLEAR, /* sunny */
+ GWEATHER_SKY_CLEAR, /* fair (night) */
+ GWEATHER_SKY_CLEAR, /* fair (day) */
+ GWEATHER_SKY_SCATTERED, /* mixed rain and hail */
+ GWEATHER_SKY_CLEAR, /* hot */
+ GWEATHER_SKY_SCATTERED, /* isolated thunderstorms */
+ GWEATHER_SKY_SCATTERED, /* scattered thunderstorms */
+ GWEATHER_SKY_SCATTERED, /* scattered thunderstorms */
+ GWEATHER_SKY_SCATTERED, /* scattered showers */
+ GWEATHER_SKY_SCATTERED, /* heavy snow */
+ GWEATHER_SKY_SCATTERED, /* scattered snow showers */
+ GWEATHER_SKY_SCATTERED, /* heavy snow */
+ GWEATHER_SKY_BROKEN, /* partly cloudy */
+ GWEATHER_SKY_SCATTERED, /* thundershowers */
+ GWEATHER_SKY_SCATTERED, /* snow showers */
+ GWEATHER_SKY_SCATTERED, /* isolated thundershowers */
+};
+
+G_STATIC_ASSERT (G_N_ELEMENTS(condition_codes) == G_N_ELEMENTS(sky_codes));
+
+static time_t
+date_to_time_t (const xmlChar *str)
+{
+ struct tm time = { 0 };
+
+ if (!strptime ((const char*) str, "%d %b %Y", &time)) {
+ g_warning ("Cannot parse date string \"%s\"", str);
+ return 0;
+ }
+
+ return mktime(&time);
+}
+
+static GWeatherInfo *
+make_info_from_node (GWeatherInfo *master_info,
+ xmlNodePtr node)
+{
+ GWeatherInfo *info;
+ GWeatherInfoPrivate *priv;
+ xmlChar *val;
+ int code;
+
+ g_return_val_if_fail (node->type == XML_ELEMENT_NODE, NULL);
+
+ info = _gweather_info_new_clone (master_info);
+ priv = info->priv;
+
+ val = xmlGetProp (node, XC("date"));
+ priv->update = date_to_time_t (val);
+ xmlFree (val);
+
+ val = xmlGetProp (node, XC("high"));
+ priv->temp_max = g_ascii_strtod ((const char*) val, NULL);
+ xmlFree (val);
+
+ val = xmlGetProp (node, XC("low"));
+ priv->temp_min = g_ascii_strtod ((const char*) val, NULL);
+ xmlFree (val);
+
+ priv->tempMinMaxValid = priv->tempMinMaxValid || (priv->temp_max > -999.0 && priv->temp_min > -999.0);
+ priv->valid = priv->tempMinMaxValid;
+
+ val = xmlGetProp (node, XC("text"));
+ priv->forecast = g_strdup ((const char*) val);
+ xmlFree (val);
+
+ val = xmlGetProp (node, XC("code"));
+ code = strtol((const char*) val, NULL, 0);
+ if (code >= 0 && code < G_N_ELEMENTS (condition_codes)) {
+ priv->cond = condition_codes[code];
+ priv->sky = sky_codes[code];
+ } else
+ priv->valid = FALSE;
+ xmlFree (val);
+
+ return info;
+}
+
+static void
+parse_forecast_xml (GWeatherInfo *master_info,
+ SoupMessageBody *body)
+{
+ GWeatherInfoPrivate *priv;
+ xmlDocPtr doc;
+ xmlXPathContextPtr xpath_ctx;
+ xmlXPathObjectPtr xpath_result;
+ int i;
+
+ priv = master_info->priv;
+
+ doc = xmlParseMemory (body->data, body->length);
+ if (!doc)
+ return;
+
+ xpath_ctx = xmlXPathNewContext (doc);
+ xmlXPathRegisterNs (xpath_ctx, XC("yweather"), XC("http://xml.weather.yahoo.com/ns/rss/1.0"));
+ xpath_result = xmlXPathEval (XC("/rss/channel/item/yweather:forecast"), xpath_ctx);
+
+ if (!xpath_result || xpath_result->type != XPATH_NODESET)
+ goto out;
+
+ for (i = 0; i < xpath_result->nodesetval->nodeNr; i++) {
+ xmlNodePtr node;
+ GWeatherInfo *info;
+
+ node = xpath_result->nodesetval->nodeTab[i];
+ info = make_info_from_node (master_info, node);
+
+ priv->forecast_list = g_slist_append (priv->forecast_list, info);
+ }
+
+ xmlXPathFreeObject (xpath_result);
+
+ out:
+ xmlXPathFreeContext (xpath_ctx);
+ xmlFreeDoc (doc);
+}
+
+static void
+yahoo_finish (SoupSession *session,
+ SoupMessage *msg,
+ gpointer user_data)
+{
+ GWeatherInfo *info = GWEATHER_INFO (user_data);
+
+ if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
+ /* forecast data is not really interesting anyway ;) */
+ g_warning ("Failed to get Yahoo! Weather forecast data: %d %s\n",
+ msg->status_code, msg->reason_phrase);
+ request_done (info, FALSE);
+ return;
+ }
+
+ parse_forecast_xml (info, msg->response_body);
+
+ request_done (info, TRUE);
+}
+
+void
+yahoo_start_open (GWeatherInfo *info)
+{
+ GWeatherInfoPrivate *priv;
+ WeatherLocation *loc;
+ gchar *url;
+ SoupMessage *message;
+
+ priv = info->priv;
+ loc = priv->location;
+
+ if (!loc || !loc->yahoo_id)
+ return;
+
+ /* Yahoo! Weather only supports forecast list
+ (and really, the other types only make sense with national
+ weather offices that cannot return structured data)
+ */
+ if (!priv->forecast_type != GWEATHER_FORECAST_LIST)
+
+ /* u=f means that the values are in imperial system (which is what
+ weather.c expects). They're converted to user preferences before
+ displaying.
+ */
+ url = g_strdup_printf("http://weather.yahooapis.com/forecastrss?w=%s&u=f", loc->yahoo_id);
+
+ message = soup_message_new ("GET", url);
+ soup_session_queue_message (priv->session, message, yahoo_finish, info);
+
+ priv->requests_pending++;
+
+ g_free (url);
+}
diff --git a/libgweather/weather.c b/libgweather/weather.c
index 7841280..e49ad1d 100644
--- a/libgweather/weather.c
+++ b/libgweather/weather.c
@@ -109,7 +109,8 @@ gweather_dpgettext (const char *context,
WeatherLocation *
_weather_location_new (const gchar *name, const gchar *code,
- const gchar *zone, const gchar *radar,
+ const gchar *zone, const gchar *yahoo_id,
+ const gchar *radar,
gboolean latlon_valid,
double latitude,
double longitude,
@@ -132,6 +133,9 @@ _weather_location_new (const gchar *name, const gchar *code,
if (radar)
location->radar = g_strdup (radar);
+ if (yahoo_id)
+ location->yahoo_id = g_strdup (yahoo_id);
+
location->latlon_valid = latlon_valid;
location->latitude = latitude;
location->longitude = longitude;
@@ -160,6 +164,9 @@ _weather_location_clone (const WeatherLocation *location)
if (location->radar)
clone->radar = g_strdup (location->radar);
+ if (location->yahoo_id)
+ clone->yahoo_id = g_strdup (location->yahoo_id);
+
clone->latlon_valid = location->latlon_valid;
clone->latitude = location->latitude;
clone->longitude = location->longitude;
@@ -174,6 +181,7 @@ _weather_location_free (WeatherLocation *location)
g_free (location->name);
g_free (location->code);
g_free (location->zone);
+ g_free (location->yahoo_id);
g_free (location->radar);
g_free (location->country_code);
g_free (location->tz_hint);
@@ -514,11 +522,17 @@ gweather_info_update (GWeatherInfo *info)
}
metar_start_open (info);
- iwin_start_open (info);
if (priv->radar) {
wx_start_open (info);
}
+
+ /* Try national forecast services first */
+ if (iwin_start_open (info))
+ return;
+
+ /* Try Yahoo! Weather next */
+ yahoo_start_open (info);
}
void
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]