diff options
author | Bastien Nocera <hadess@hadess.net> | 2007-11-22 23:17:12 +0000 |
---|---|---|
committer | Bastien Nocera <hadess@src.gnome.org> | 2007-11-22 23:17:12 +0000 |
commit | 32e0353d86ffc2df26b19608a680b19cfa3cc04f (patch) | |
tree | b83dc88043b3a28c91e9851b189edf219ed5f50d /libgweather | |
parent | 09ba31d4c2742b14ac0a75e12e15ac9fc784cbb9 (diff) | |
download | libgweather-32e0353d86ffc2df26b19608a680b19cfa3cc04f.tar.gz |
Add, mostly copied from gnome-applets
2007-11-22 Bastien Nocera <hadess@hadess.net>
* Makefile.am:
* autogen.sh:
* configure.in: Add, mostly copied from gnome-applets
2007-11-22 Bastien Nocera <hadess@hadess.net>
* *: Move and update from gnome-applets
2007-11-22 Bastien Nocera <hadess@hadess.net>
* *: Import from gnome-applets trunk
svn path=/trunk/; revision=3
Diffstat (limited to 'libgweather')
-rw-r--r-- | libgweather/AUTHORS | 5 | ||||
-rw-r--r-- | libgweather/ChangeLog | 260 | ||||
-rw-r--r-- | libgweather/Makefile.am | 72 | ||||
-rw-r--r-- | libgweather/README | 7 | ||||
-rw-r--r-- | libgweather/gweather-gconf.c | 259 | ||||
-rw-r--r-- | libgweather/gweather-gconf.h | 75 | ||||
-rw-r--r-- | libgweather/gweather-prefs.c | 296 | ||||
-rw-r--r-- | libgweather/gweather-prefs.h | 53 | ||||
-rw-r--r-- | libgweather/gweather.pc.in | 11 | ||||
-rw-r--r-- | libgweather/gweather.schemas.in | 173 | ||||
-rw-r--r-- | libgweather/test_metar.c | 78 | ||||
-rw-r--r-- | libgweather/weather-bom.c | 125 | ||||
-rw-r--r-- | libgweather/weather-iwin.c | 204 | ||||
-rw-r--r-- | libgweather/weather-met.c | 238 | ||||
-rw-r--r-- | libgweather/weather-metar.c | 590 | ||||
-rw-r--r-- | libgweather/weather-priv.h | 211 | ||||
-rw-r--r-- | libgweather/weather-sun.c | 247 | ||||
-rw-r--r-- | libgweather/weather-wx.c | 128 | ||||
-rw-r--r-- | libgweather/weather.c | 1090 | ||||
-rw-r--r-- | libgweather/weather.h | 159 |
20 files changed, 4281 insertions, 0 deletions
diff --git a/libgweather/AUTHORS b/libgweather/AUTHORS new file mode 100644 index 0000000..15f2d9f --- /dev/null +++ b/libgweather/AUTHORS @@ -0,0 +1,5 @@ +The library: +Philip Langdale <philipl@mail.utexas.edu> + +Almost all of the code is refactored from the gweather applet, +so checkout that AUTHORS file too. diff --git a/libgweather/ChangeLog b/libgweather/ChangeLog new file mode 100644 index 0000000..50460e1 --- /dev/null +++ b/libgweather/ChangeLog @@ -0,0 +1,260 @@ +2007-11-22 Bastien Nocera <hadess@hadess.net> + + * *: Import from gnome-applets trunk + +2007-11-16 Callum McKenzie <callum@spooky-possum.org> + + * weather-iwin.c: Removed unused (#if 0) code, look in SVN if it + turns out to have been important. Prevent a random space appearing + in the forecast (patch from Michael Vrable, Bug #497204). Add a + bit of explanation about what that piece of code is trying to do. + +2007-11-04 Callum McKenzie <callum@spooky-possum.org> + + * gweather-prefs.c: Change the guard #ifdefs for nl_langinfo stuff + so that autoconfig works correctly. Patch from Matthias Clasen + (Bug #491438). Also added an #ifdef around the langingo.h #include + in case the file isn't present at all (Mac OS X?). + + * weather.c (weather_info_abort): Ensure the requests_pending flag + is cleared on abort. Another Matias Clasen special (Bug #491437). + + * weather.h (weather_info_update): Remove a trailing ; from the + macro. Spotted by Matthias Clasen (Bug #491435). + +2007-10-13 Callum McKenzie <callum@spooky-possum.org> + + * weather.c (temperature_string): Use appropriate unicode Degree + Celsius and Degree Fahrenheit symbols instead of the generic + Degree symbol. Patch from Alex Jones (Bug #468887). + + * gweather.pc.in (Libs): Make sure the library is linked in. Patch + courtesy of Matthias Clasen (bug #479172). Also added -lm to the + libs list for completeness. + +2007-07-12 Callum McKenzie <callum@spooky-possum.org> + + * weather-metar.c (make_time): Fix the logic to only fiddle the + day number on the 1st. Based on Elliott Hughes patch from bug + #455012. Improved comments. + +2007-07-11 Callum McKenzie <callum@spooky-possum.org> + + * weather-sun.c (calc_sun): Use the current time rather than the + observation time to calculate the sunrise and sunset times. Fixes + the case where the observation gets horribly out of date. See bug + #455012. + +2007-05-13 Kjartan Maraas <kmaraas@gnome.org> + + * Makefile.am: Add GNOME_APPLETS_CFLAGS to get + GConf includes. Fixes the build for me. + +2007-01-05 Kjartan Maraas <kmaraas@gnome.org> + + * weather-met.c: s/malloc.h/stdlib.h/ + Closes bug #387179. Patch from Roy Marples. + +2006-08-07 Davyd Madeley <davyd@madeley.id.au> + + * weather-bom.c: + * weather-met.c: + - catch possible NULL pointers when parsing strings. Patches from + Kevin Bauder <kevin.bauder@gmail.com>. Closes #170628. + +2006-07-23 Davyd Madeley <davyd@madeley.id.au> + + * gweather.schemas.in: update schema descriptions, patch from + Adam Petaccia. Closes #167195. + +2006-07-02 Kjartan Maraas <kmaraas@gnome.org> + + * Makefile.am: Cygwin portability. Patch from Cygwin Ports Maintainer. + Closes bug #341495. + +2006-01-31 Davyd Madeley <davyd@madeley.id.au> + + * weather.c: replace sscanf with g_strsplit to prevent stack smashing. + Closes original #327406. + +2006-01-17 Theppitak Karoonboonyanan <thep@linux.thai.net> + + * gweather-prefs.c: add "atm" as possible unit in translator comment + for DEFAULT_PRESSURE_UNIT. Closes #327275. + +2006-01-15 Davyd Madeley <davyd@madeley.id.au> + + * gweather-prefs.c: + * weather-priv.h: + * weather.c: + * weather.h: add atmospheres as a unit of pressure + Patch from Alexandros Frantzis <alfius@freemail.gr>. Closes + #325447. + +2006-01-15 Davyd Madeley <davyd@madeley.id.au> + + * weather-sun.c: fix a crasher that was hinted to at a compiler + warning + +2006-01-15 Davyd Madeley <davyd@madeley.id.au> + + * Makefile.am: + * test_metar.c: New program to test station report parsing + * weather-metar.c: (metar_parse): remove 'static' + * weather-priv.h: add metar_parse() + + Patch from Frank Solensky <frank@solensky.org>. Closes #144792. + +2006-01-15 Davyd Madeley <davyd@madeley.id.au> + + * weather.c: + * weather-priv.h: move prototype for calc_sun to header and remove + static declaration. + +2006-01-12 Frank Solensky <frank@solensky.org> + + * Makefile.am: + * weather.h: + * weather.c: + * weather-sun.c: move sun calculations out to new source file + +2005-12-31 Davyd Madeley <davyd@madeley.id.au> + + * weather-metar.c: (metar_tok_temp): + * weather.c: (calc_humidity): dewpoint is optional + Closes gweather bug 172711. Patch from Frank Solensky + <frank@solensky.org> + + * gweather-gconf.c: looks like the last commit stopped this compiling, + fixed it up + +2005-12-31 Simos Xenitellis <simos@gnome.org> + + * gweather-gconf.c: Added translator comments for the DEFAULT_* + strings, the strings to specify the default location per locale. + +2005-12-11 Philip Langdale <philipl@mail.utexas.edu> + + A gweather-gconf.c + A gweather-gconf.h + A gweather-prefs.c + A gweather-prefs.h + A gweather.schemas.in: Merge libgweatherprefs into + libgweather. The extra library isn't worth the hassle + considering that gconf is already a dependency of + libgweather and we currently have no clients that want + the backend without the prefs. + + I have merged the libgweathepref ChangeLong entries. + + * .cvsignore + * ChangeLog + * Makefile.am + * README + * weather-iwin.c + * weather-metar.c + * weather-wx.c + * weather.c: Update to reflect the merged files. + + A AUTHORS: + R MAINTAINERS: A more accurate description of what + I want to claim at this time. :-) + +2005-12-04 Philip Langdale <philipl@mail.utexas.edu> + + * gweather-gconf.c + * gweather-gconf.h: Add gweather_gconf_get_client + to provide access to the GConfClient. + + * gweather-prefs.c: make gweather_prefs_load work + correctly when called multiple times. The old + radar url was not previously being freed. + +2005-11-27 Philip Langdale <philipl@mail.utexas.edu> + + Initial checkin of libgweatherprefs. + + A .cvsignore + A ChangeLog + A MAINTAINERS + A Makefile.am + A README: Usual building blocks. + + A gweather.schemas.in: Brought the schemas over + unchanged from gweather. + + A gweatherprefs.pc.in: pkgconfig descriptor. + + A gweather-gconf.c + A gweather-gconf.h: Primarily, this encapsulates + the 'current path' notion in an identical way + to libpanelapplet. It is also the new home of + 'weather_location_config_read' which files out + a WeatherLocation struct based on the values in + gconf. + + A gweather-prefs.c + A gweather-prefs.h: This is, essentially unchanged, + the 'gweather_pref_load' function along with the + enum-to-string helpers it depends on. Obviously + the gconf access now uses the gweather-gconf + methods rather than the panel applet ones. + +2005-11-27 Philip Langdale <philipl@mail.utexas.edu> + + A .cvsignore: + A Makefile.am: The things no self respecting directory + can be without. + + A gweather.pc.in: pkgconfig descriptor. + + * weather.c: Remove entanglement with the gweather applet: + - Introduce a simple WeatherPrefs struct to replace the + direct gconf access. The caller can set these prefs as + they wish (reading from gconf is obviously a possibility). + This struct also encapsulates the two odd-man-out global + variables - so the library is now safe for multiple + instances of WeatherInfo. + - Actually use the WeatherInfoFunc callback instead of + hardcoding the applet callback. + - Refactor the cancelling of an outstanding retrieval + into a public abort method. + - Update the weather_info_[new|update] methods to take + the WeatherPrefs parameter and return the WeatherInfo + rather than directly storing it in GWeatherApplet. + The boolean return semantics are retained in that + NULL will be returned if the call fails. + + A weather-priv.h + * weather.h: The refactored header. Now private calls + and the WeatherInfo definition are actually kept private. + Gratuitous 'extern' keywords removed. + + * weather-bom.c + * weather-iwin.c + * weather-met.c + * weather-metar.c + * weather-wx.c: The service-specific backend files are + only minimally modified: + - Added Copyright header from weather.c + - Fixed any warnings reported by -Wall + - Included weather-priv.h + - Minimised required header set + - Updated to reflect that WeatherInfo is passed as + callback data instead of GWeatherApplet. + +2005-11-27 Philip Langdale <philipl@mail.utexas.edu> + + * ChangeLog + * MAINTAINERS + * README + * weather-bom.c + * weather.c + * weather.h + * weather-iwin.c + * weather-metar.c + * weather-met.c + * weather-wx.c: Initial checkin of libgweather. + I'm checking these in first so that there's a an easy way + to see the diff to the versions of these files that actually + compile. diff --git a/libgweather/Makefile.am b/libgweather/Makefile.am new file mode 100644 index 0000000..a41601e --- /dev/null +++ b/libgweather/Makefile.am @@ -0,0 +1,72 @@ +LIBRARY_VERSION = 0:0:0 + +lib_LTLIBRARIES = libgweather.la + +libgweatherincdir = $(includedir)/libgweather +libgweatherinc_HEADERS = weather.h gweather-gconf.h gweather-prefs.h + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = gweather.pc + +libgweather_la_SOURCES = \ + weather.c weather.h weather-priv.h \ + weather-metar.c weather-iwin.c weather-met.c \ + weather-bom.c weather-wx.c \ + weather-sun.c \ + gweather-prefs.c gweather-prefs.h \ + gweather-gconf.c gweather-gconf.h + +libgweather_la_CFLAGS = \ + -I$(top_srcdir) \ + -I$(srcdir) \ + $(WARN_CFLAGS) \ + $(GTK_CFLAGS) \ + $(GNOME_VFS_APPLETS_CFLAGS) \ + $(GNOME_APPLETS_CFLAGS) \ + -DG_LOG_DOMAIN=\"GWeather\" \ + -DGNOMELOCALEDIR=\""$(datadir)/locale"\" + +libgweather_la_LIBADD = \ + $(GTK_LIBS) \ + $(GNOME_VFS_APPLETS_LIBS) + +libgweather_la_LDFLAGS = \ + -version-info $(LIBRARY_VERSION) -no-undefined + +test_metar_SOURCES = test_metar.c + +test_metar_CFLAGS = \ + -I$(top_srcdir) \ + -I$(srcdir) \ + $(WARN_CFLAGS) \ + $(GTK_CFLAGS) \ + $(GNOME_APPLETS_CFLAGS) \ + $(GNOME_VFS_APPLETS_CFLAGS) \ + -DG_LOG_DOMAIN=\"GWeather\" + +test_metar_LDADD = \ + $(GNOME_APPLETS_LIB) \ + $(GNOME_VFS_APPLETS_LIB) \ + libgweather.la + +noinst_HEADERS = weather-priv.h +noinst_PROGRAMS = test_metar + +schemasdir = @GCONF_SCHEMA_FILE_DIR@ +schemas_in_files = gweather.schemas.in +schemas_DATA = $(schemas_in_files:.schemas.in=.schemas) + +@INTLTOOL_SCHEMAS_RULE@ + +if GCONF_SCHEMAS_INSTALL +install-data-local: + GCONF_CONFIG_SOURCE=$(GCONF_SCHEMA_CONFIG_SOURCE) $(GCONFTOOL) --makefile-install-rule $(schemas_DATA) ; +uninstall-local: + GCONF_CONFIG_SOURCE=$(GCONF_SCHEMA_CONFIG_SOURCE) $(GCONFTOOL) --makefile-uninstall-rule $(schemas_DATA) ; +endif + +EXTRA_DIST = gweather.pc.in $(schemas_in_files) + +EXTRA_PROGRAMS = test_metar + +CLEANFILES = $(schemas_DATA) $(EXTRA_PROGRAMS) diff --git a/libgweather/README b/libgweather/README new file mode 100644 index 0000000..989c0cd --- /dev/null +++ b/libgweather/README @@ -0,0 +1,7 @@ +libgweather +----------- + +libgweather is the retrieval backend from the gweather gnome-applet. +It's generally useful for applications that need to get weather +information. It also includes helper functions to access the gweather +gconf schema. diff --git a/libgweather/gweather-gconf.c b/libgweather/gweather-gconf.c new file mode 100644 index 0000000..af4c063 --- /dev/null +++ b/libgweather/gweather-gconf.c @@ -0,0 +1,259 @@ +/* + * gweather-gconf.c: GConf interaction methods for gweather. + * + * Copyright (C) 2005 Philip Langdale, Papadimitriou Spiros + * + * This library 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 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: + * Philip Langdale <philipl@mail.utexas.edu> + * Papadimitriou Spiros <spapadim+@cs.cmu.edu> + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <string.h> +#include <glib/gi18n-lib.h> +#include <libgweather/gweather-gconf.h> + +struct _GWeatherGConf +{ + GConfClient *gconf; + char *prefix; +}; + + +GWeatherGConf * +gweather_gconf_new(const char *prefix) +{ + GWeatherGConf *ctx = g_new0(GWeatherGConf, 1); + ctx->gconf = gconf_client_get_default(); + ctx->prefix = g_strdup(prefix); + + return ctx; +} + + +void +gweather_gconf_free(GWeatherGConf *ctx) +{ + g_object_unref(ctx->gconf); + g_free(ctx->prefix); + g_free(ctx); +} + + +GConfClient * +gweather_gconf_get_client(GWeatherGConf *ctx) +{ + return ctx->gconf; +} + + +gchar * +gweather_gconf_get_full_key (GWeatherGConf *ctx, + const gchar *key) +{ + return g_strdup_printf ("%s/%s", ctx->prefix, key); +} + +void +gweather_gconf_set_bool (GWeatherGConf *ctx, + const gchar *key, + gboolean the_bool, + GError **opt_error) +{ + gchar *full_key = gweather_gconf_get_full_key (ctx, key); + gconf_client_set_bool (ctx->gconf, full_key, the_bool, opt_error); + g_free (full_key); +} + +void +gweather_gconf_set_int (GWeatherGConf *ctx, + const gchar *key, + gint the_int, + GError **opt_error) +{ + gchar *full_key = gweather_gconf_get_full_key (ctx, key); + gconf_client_set_int (ctx->gconf, full_key, the_int, opt_error); + g_free (full_key); +} + +void +gweather_gconf_set_string (GWeatherGConf *ctx, + const gchar *key, + const gchar *the_string, + GError **opt_error) +{ + gchar *full_key = gweather_gconf_get_full_key (ctx, key); + gconf_client_set_string (ctx->gconf, full_key, the_string, opt_error); + g_free (full_key); +} + +gboolean +gweather_gconf_get_bool (GWeatherGConf *ctx, + const gchar *key, + GError **opt_error) +{ + gchar *full_key = gweather_gconf_get_full_key (ctx, key); + gboolean ret = gconf_client_get_bool (ctx->gconf, full_key, opt_error); + g_free (full_key); + return ret; +} + +gint +gweather_gconf_get_int (GWeatherGConf *ctx, + const gchar *key, + GError **opt_error) +{ + gchar *full_key = gweather_gconf_get_full_key (ctx, key); + gint ret = gconf_client_get_int (ctx->gconf, full_key, opt_error); + g_free (full_key); + return ret; +} + +gchar * +gweather_gconf_get_string (GWeatherGConf *ctx, + const gchar *key, + GError **opt_error) +{ + gchar *full_key = gweather_gconf_get_full_key (ctx, key); + gchar *ret = gconf_client_get_string (ctx->gconf, full_key, opt_error); + g_free (full_key); + return ret; +} + + +WeatherLocation * +gweather_gconf_get_location(GWeatherGConf *ctx) +{ + WeatherLocation *location; + gchar *name, *code, *zone, *radar, *coordinates; + + name = gweather_gconf_get_string (ctx, "location4", NULL); + if (!name) + { + /* TRANSLATOR: Change this to the default location name, + * used when you first start the Weather Applet. This is + * the common localised name that corresponds to + * the location code (DEFAULT_CODE) you will put on the next message + * For example, for the Greek locale, we set this to "Athens", the + * capital city and we write it in Greek. It's important to translate + * this name. + * + * If you do not require a DEFAULT_LOCATION, set this to + * "DEFAULT_LOCATION". + */ + if (strcmp ("DEFAULT_LOCATION", _("DEFAULT_LOCATION"))) + name = g_strdup (_("DEFAULT_LOCATION")); + else + name = g_strdup ("Pittsburgh"); + } + + code = gweather_gconf_get_string (ctx, "location1", NULL); + if (!code) + { + /* TRANSLATOR: Change this to the code of your default location that + * corresponds to the DEFAULT_LOCATION name you put above. This is + * normally a four-letter (ICAO) code and can be found in + * http://cvs.gnome.org/viewcvs/gnome-applets/gweather/Locations.xml.in + * NB. The web page is over 1.7MB in size. + * Pick a default location like a capital city so that it would be ok + * for more of your users. For example, for Greek, we use "LGAV" for + * the capital city, Athens. + * + * If you do not require a DEFAULT_CODE, set this to "DEFAULT_CODE". + */ + if (strcmp ("DEFAULT_CODE", _("DEFAULT_CODE"))) + code = g_strdup (_("DEFAULT_CODE")); + else + code = g_strdup ("KPIT"); + } + + zone = gweather_gconf_get_string (ctx, "location2", NULL); + if (!zone) + { + /* TRANSLATOR: Change this to the zone of your default location that + * corresponds to the DEFAULT_LOCATION and DEFAULT_CODE you put above. + * Normally, US and Canada locations have zones while the rest do not. + * Check + * http://cvs.gnome.org/viewcvs/gnome-applets/gweather/Locations.xml.in + * as any zone you put here must also be present in the Locations.xml + * file. + * + * If your default location does not have a zone, set this to + * "DEFAULT_ZONE". + */ + if (strcmp ("DEFAULT_ZONE", _("DEFAULT_ZONE"))) + zone = g_strdup (_("DEFAULT_ZONE" )); + else + zone = g_strdup ("PAZ021"); + } + + radar = gweather_gconf_get_string(ctx, "location3", NULL); + if (!radar) + { + /* TRANSLATOR: Change this to the radar of your default location that + * corresponds to the DEFAULT_LOCATION and DEFAULT_CODE you put above. + * Normally, US and Canada locations have radar names while the rest do + * not. Check + * http://cvs.gnome.org/viewcvs/gnome-applets/gweather/Locations.xml.in + * as any radar you put here must also be present in the Locations.xml + * file. + * + * If your default location does not have a radar, set this to " " + * (or space). + * If you do not have a default location, set this to DEFAULT_RADAR. + */ + if (strcmp ("DEFAULT_RADAR", _("DEFAULT_RADAR"))) + radar = g_strdup (_("DEFAULT_RADAR")); + else + radar = g_strdup ("pit"); + } + + coordinates = gweather_gconf_get_string (ctx, "coordinates", NULL); + if (!coordinates) + { + /* TRANSLATOR: Change this to the coordinates of your default location + * that corresponds to the DEFAULT_LOCATION and DEFAULT_CODE you put + * above. Check + * http://cvs.gnome.org/viewcvs/gnome-applets/gweather/Locations.xml.in + * as any coordinates you put here must also be present in the + * Locations.xml file. + * + * If your default location does not have known coordinates, set this + * to " " (or space). + * If you do not have a default location, set this to + * DEFAULT_COORDINATES. + */ + if (strcmp ("DEFAULT_COORDINATES", _("DEFAULT_COORDINATES"))) + coordinates = g_strdup (_("DEFAULT_COORDINATES")); + else + coordinates = g_strdup ("40-32N 080-13W"); + } + + location = weather_location_new (name, code, zone, radar, coordinates); + + g_free (name); + g_free (code); + g_free (zone); + g_free (radar); + g_free (coordinates); + + return location; +} diff --git a/libgweather/gweather-gconf.h b/libgweather/gweather-gconf.h new file mode 100644 index 0000000..e5446a2 --- /dev/null +++ b/libgweather/gweather-gconf.h @@ -0,0 +1,75 @@ +/* + * gweather-gconf.h: GConf interaction methods for gweather. + * + * Copyright (C) 2005 Philip Langdale, Papadimitriou Spiros + * + * This library 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 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: + * Philip Langdale <philipl@mail.utexas.edu> + * Papadimitriou Spiros <spapadim+@cs.cmu.edu> + */ + +#ifndef __GWEATHER_GCONF_WRAPPER_H__ +#define __GWEATHER_GCONF_WRAPPER_H__ + +#include <glib/gmacros.h> +#include <glib/gerror.h> +#include <gconf/gconf-client.h> +#include <gconf/gconf-value.h> + +#include <libgweather/weather.h> + +G_BEGIN_DECLS + +typedef struct _GWeatherGConf GWeatherGConf; + +GWeatherGConf * gweather_gconf_new (const char *prefix); +void gweather_gconf_free (GWeatherGConf *ctx); + +GConfClient * gweather_gconf_get_client (GWeatherGConf *ctx); + +WeatherLocation * gweather_gconf_get_location (GWeatherGConf *ctx); + +gchar * gweather_gconf_get_full_key (GWeatherGConf *ctx, + const gchar *key); + +void gweather_gconf_set_bool (GWeatherGConf *ctx, + const gchar *key, + gboolean the_bool, + GError **opt_error); +void gweather_gconf_set_int (GWeatherGConf *ctx, + const gchar *key, + gint the_int, + GError **opt_error); +void gweather_gconf_set_string (GWeatherGConf *ctx, + const gchar *key, + const gchar *the_string, + GError **opt_error); + +gboolean gweather_gconf_get_bool (GWeatherGConf *ctx, + const gchar *key, + GError **opt_error); +gint gweather_gconf_get_int (GWeatherGConf *ctx, + const gchar *key, + GError **opt_error); +gchar * gweather_gconf_get_string (GWeatherGConf *ctx, + const gchar *key, + GError **opt_error); + +G_END_DECLS + +#endif /* __GWEATHER_GCONF_WRAPPER_H__ */ diff --git a/libgweather/gweather-prefs.c b/libgweather/gweather-prefs.c new file mode 100644 index 0000000..d24e180 --- /dev/null +++ b/libgweather/gweather-prefs.c @@ -0,0 +1,296 @@ +/* + * Papadimitriou Spiros <spapadim+@cs.cmu.edu> + * + * This code released under the GNU GPL. + * Read the file COPYING for more information. + * + * Preference handling functions. + * + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#ifdef HAVE__NL_MEASUREMENT_MEASUREMENT +#include <langinfo.h> +#endif + +#include <glib/gi18n-lib.h> +#include <gconf/gconf-client.h> +#include <libgweather/gweather-prefs.h> + +static GConfEnumStringPair temp_unit_enum_map [] = { + { TEMP_UNIT_DEFAULT, "Default" }, + { TEMP_UNIT_KELVIN, "K" }, + { TEMP_UNIT_CENTIGRADE, "C" }, + { TEMP_UNIT_FAHRENHEIT, "F" }, + { 0, NULL } +}; + +static GConfEnumStringPair speed_unit_enum_map [] = { + { SPEED_UNIT_DEFAULT, "Default" }, + { SPEED_UNIT_MS, "m/s" }, + { SPEED_UNIT_KPH, "km/h" }, + { SPEED_UNIT_MPH, "mph" }, + { SPEED_UNIT_KNOTS, "knots" }, + { SPEED_UNIT_BFT, "Beaufort scale" }, + { 0, NULL } +}; + +static GConfEnumStringPair pressure_unit_enum_map [] = { + { PRESSURE_UNIT_DEFAULT, "Default" }, + { PRESSURE_UNIT_KPA, "kPa" }, + { PRESSURE_UNIT_HPA, "hPa" }, + { PRESSURE_UNIT_MB, "mb" }, + { PRESSURE_UNIT_MM_HG, "mmHg" }, + { PRESSURE_UNIT_INCH_HG, "inHg" }, + { PRESSURE_UNIT_ATM, "atm" }, + { 0, NULL } +}; + +static GConfEnumStringPair distance_unit_enum_map [] = { + { DISTANCE_UNIT_DEFAULT, "Default" }, + { DISTANCE_UNIT_METERS, "m" }, + { DISTANCE_UNIT_KM, "km" }, + { DISTANCE_UNIT_MILES, "mi" }, + { 0, NULL } +}; + + +static void parse_temp_string (const gchar *gconf_str, GWeatherPrefs *prefs) +{ + gint value = 0; +#ifdef HAVE__NL_MEASUREMENT_MEASUREMENT + char *imperial = NULL; +#endif + + prefs->use_temperature_default = TRUE; + + if ( gconf_str && gconf_string_to_enum (temp_unit_enum_map, gconf_str, &value) ) { + prefs->temperature_unit = value; + + if ((prefs->temperature_unit == TEMP_UNIT_DEFAULT) && + (gconf_string_to_enum (temp_unit_enum_map, _("DEFAULT_TEMP_UNIT"), &value)) ) { + prefs->temperature_unit = value; + } else { + prefs->use_temperature_default = FALSE; + } + } + else { + /* TRANSLATOR: This is the default unit to use for temperature measurements. */ + /* Valid values are: "K" (Kelvin), "C" (Celsius) and "F" (Fahrenheit) */ + if (gconf_string_to_enum (temp_unit_enum_map, _("DEFAULT_TEMP_UNIT"), &value) ) { + prefs->temperature_unit = value; + } + } + if (!prefs->temperature_unit || prefs->temperature_unit == TEMP_UNIT_DEFAULT ) { +#ifdef HAVE__NL_MEASUREMENT_MEASUREMENT + imperial = nl_langinfo(_NL_MEASUREMENT_MEASUREMENT); + if ( imperial && imperial[0] == 2 ) { + /* imperial */ + prefs->temperature_unit = TEMP_UNIT_FAHRENHEIT; + } else +#endif + prefs->temperature_unit = TEMP_UNIT_CENTIGRADE; + } +} + +static void parse_speed_string (const gchar *gconf_str, GWeatherPrefs *prefs) +{ + gint value = 0; +#ifdef HAVE__NL_MEASUREMENT_MEASUREMENT + char *imperial = NULL; +#endif + + prefs->use_speed_default = TRUE; + + if ( gconf_str && gconf_string_to_enum (speed_unit_enum_map, gconf_str, &value) ) { + prefs->speed_unit = value; + if ((prefs->speed_unit == SPEED_UNIT_DEFAULT) && + (gconf_string_to_enum (speed_unit_enum_map, _("DEFAULT_SPEED_UNIT"), &value)) ) { + prefs->speed_unit = value; + } else { + prefs->use_speed_default = FALSE; + } + } + else { + /* TRANSLATOR: This is the default unit to use for wind speed. */ + /* Valid values are: "m/s" (meters per second), "km/h" (kilometers per hour), */ + /* "mph" (miles per hour) and "knots" */ + if (gconf_string_to_enum (speed_unit_enum_map, _("DEFAULT_SPEED_UNIT"), &value) ) { + prefs->speed_unit = value; + } + } + if ( (!prefs->speed_unit) || prefs->speed_unit == SPEED_UNIT_DEFAULT ) { +#ifdef HAVE__NL_MEASUREMENT_MEASUREMENT + imperial = nl_langinfo(_NL_MEASUREMENT_MEASUREMENT); + if ( imperial && imperial[0] == 2 ) { + /* imperial */ + prefs->speed_unit = SPEED_UNIT_KNOTS; + } else +#endif + prefs->speed_unit = SPEED_UNIT_MS; + } +} + + +static void parse_pressure_string (const gchar *gconf_str, GWeatherPrefs *prefs) +{ + gint value = 0; +#ifdef _NL_MEASUREMENT_MEASUREMENT + char *imperial = NULL; +#endif + + prefs->use_pressure_default = TRUE; + + if ( gconf_str && gconf_string_to_enum (pressure_unit_enum_map, gconf_str, &value) ) { + prefs->pressure_unit = value; + + if ((prefs->pressure_unit == PRESSURE_UNIT_DEFAULT) && + (gconf_string_to_enum (pressure_unit_enum_map, _("DEFAULT_PRESSURE_UNIT"), &value)) ) { + prefs->pressure_unit = value; + } else { + prefs->use_pressure_default = FALSE; + } + } + else { + /* TRANSLATOR: This is the default unit to use for atmospheric pressure. */ + /* Valid values are: "kPa" (kiloPascals), "hPa" (hectoPascals), + "mb" (millibars), "mmHg" (millimeters of mercury), + "inHg" (inches of mercury) and "atm" (atmosphere) */ + if (gconf_string_to_enum (pressure_unit_enum_map, _("DEFAULT_PRESSURE_UNIT"), &value) ) { + prefs->pressure_unit = value; + } + } + if ( (!prefs->pressure_unit) || prefs->pressure_unit == PRESSURE_UNIT_DEFAULT ) { +#ifdef _NL_MEASUREMENT_MEASUREMENT + imperial = nl_langinfo(_NL_MEASUREMENT_MEASUREMENT); + if ( imperial && imperial[0] == 2 ) { + /* imperial */ + prefs->pressure_unit = PRESSURE_UNIT_INCH_HG; + } else +#endif + prefs->pressure_unit = PRESSURE_UNIT_HPA; + } +} + +static void parse_distance_string (const gchar *gconf_str, GWeatherPrefs *prefs) +{ + gint value = 0; +#ifdef _NL_MEASUREMENT_MEASUREMENT + char *imperial = NULL; +#endif + + prefs->use_distance_default = TRUE; + if ( gconf_str && gconf_string_to_enum (distance_unit_enum_map, gconf_str, &value) ) { + prefs->distance_unit = value; + + if ((prefs->distance_unit == DISTANCE_UNIT_DEFAULT) && + (gconf_string_to_enum (distance_unit_enum_map, _("DEFAULT_DISTANCE_UNIT"), &value)) ) { + prefs->distance_unit = value; + } else { + prefs->use_distance_default = FALSE; + } + } + else { + /* TRANSLATOR: This is the default unit to use for visibility distance. */ + /* Valid values are: "m" (meters), "km" (kilometers) and "mi" (miles) */ + if (gconf_string_to_enum (distance_unit_enum_map, _("DEFAULT_DISTANCE_UNIT"), &value) ) { + prefs->distance_unit = value; + } + } + + if ((!prefs->distance_unit) || prefs->distance_unit == DISTANCE_UNIT_DEFAULT ) { +#ifdef _NL_MEASUREMENT_MEASUREMENT + imperial = nl_langinfo(_NL_MEASUREMENT_MEASUREMENT); + if ( imperial && imperial[0] == 2 ) { + /* imperial */ + prefs->distance_unit = DISTANCE_UNIT_MILES; + } else +#endif + prefs->distance_unit = DISTANCE_UNIT_METERS; + } + + return; +} + +const char *gweather_prefs_temp_enum_to_string (TempUnit temp) +{ + return gconf_enum_to_string(temp_unit_enum_map, temp); +} + +const char *gweather_prefs_speed_enum_to_string (SpeedUnit speed) +{ + return gconf_enum_to_string(speed_unit_enum_map, speed); +} + +const char *gweather_prefs_pressure_enum_to_string (PressureUnit pressure) +{ + return gconf_enum_to_string(pressure_unit_enum_map, pressure); +} + +const char *gweather_prefs_distance_enum_to_string (DistanceUnit distance) +{ + return gconf_enum_to_string(distance_unit_enum_map, distance); +} + + +void gweather_prefs_load (GWeatherPrefs *prefs, GWeatherGConf *ctx) +{ + GError *error = NULL; + gchar *gconf_str = NULL; + + if (prefs->location) { + weather_location_free(prefs->location); + } + prefs->location = gweather_gconf_get_location(ctx); + + /* Assume we use unit defaults */ + prefs->use_temperature_default = TRUE; + prefs->use_speed_default = TRUE; + prefs->use_pressure_default = TRUE; + prefs->use_distance_default = TRUE; + + prefs->update_interval = + gweather_gconf_get_int(ctx, "auto_update_interval", &error); + if (error) { + g_print ("%s \n", error->message); + g_error_free (error); + error = NULL; + } + prefs->update_interval = MAX (prefs->update_interval, 60); + prefs->update_enabled = + gweather_gconf_get_bool(ctx, "auto_update", NULL); + prefs->detailed = + gweather_gconf_get_bool(ctx, "enable_detailed_forecast", NULL); + prefs->radar_enabled = + gweather_gconf_get_bool(ctx, "enable_radar_map", NULL); + prefs->use_custom_radar_url = + gweather_gconf_get_bool(ctx, "use_custom_radar_url", NULL); + + if (prefs->radar) { + g_free(prefs->radar); + prefs->radar = NULL; + } + prefs->radar = gweather_gconf_get_string (ctx, "radar", NULL); + + gconf_str = gweather_gconf_get_string (ctx, GCONF_TEMP_UNIT, NULL); + parse_temp_string(gconf_str, prefs); + g_free (gconf_str); + + gconf_str = gweather_gconf_get_string (ctx, GCONF_SPEED_UNIT, NULL); + parse_speed_string(gconf_str, prefs); + g_free (gconf_str); + + gconf_str = gweather_gconf_get_string (ctx, GCONF_PRESSURE_UNIT, NULL); + parse_pressure_string(gconf_str, prefs); + g_free (gconf_str); + + gconf_str = gweather_gconf_get_string (ctx, GCONF_DISTANCE_UNIT, NULL); + parse_distance_string(gconf_str, prefs); + g_free (gconf_str); + + return; +} + diff --git a/libgweather/gweather-prefs.h b/libgweather/gweather-prefs.h new file mode 100644 index 0000000..44824ff --- /dev/null +++ b/libgweather/gweather-prefs.h @@ -0,0 +1,53 @@ +/* + * Papadimitriou Spiros <spapadim+@cs.cmu.edu> + * + * This code released under the GNU GPL. + * Read the file COPYING for more information. + * + * Preference handling functions. + * + */ + +#ifndef __GWEATHER_PREFS_H_ +#define __GWEATHER_PREFS_H_ + +#include <libgweather/weather.h> +#include <libgweather/gweather-gconf.h> + +/* gconf keys */ +#define GCONF_TEMP_UNIT "temperature_unit" +#define GCONF_SPEED_UNIT "speed_unit" +#define GCONF_PRESSURE_UNIT "pressure_unit" +#define GCONF_DISTANCE_UNIT "distance_unit" + +typedef struct _GWeatherPrefs GWeatherPrefs; + +struct _GWeatherPrefs { + WeatherLocation *location; + gint update_interval; /* in seconds */ + gboolean update_enabled; + gboolean detailed; + gboolean radar_enabled; + gboolean use_custom_radar_url; + gchar *radar; + + TempUnit temperature_unit; + gboolean use_temperature_default; + SpeedUnit speed_unit; + gboolean use_speed_default; + PressureUnit pressure_unit; + gboolean use_pressure_default; + DistanceUnit distance_unit; + gboolean use_distance_default; +}; + +void gweather_prefs_load (GWeatherPrefs *prefs, + GWeatherGConf *ctx); + +const char * gweather_prefs_temp_enum_to_string (TempUnit temp); +const char * gweather_prefs_speed_enum_to_string (SpeedUnit speed); +const char * gweather_prefs_pressure_enum_to_string (PressureUnit pressure); +const char * gweather_prefs_distance_enum_to_string (DistanceUnit distance); + + +#endif /* __GWEATHER_PREFS_H_ */ diff --git a/libgweather/gweather.pc.in b/libgweather/gweather.pc.in new file mode 100644 index 0000000..d9aaf00 --- /dev/null +++ b/libgweather/gweather.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: GWeather +Description: GWeather shared library +Version: @VERSION@ +Requires: gtk+-2.0, gnome-vfs-2.0 +Libs: -L${libdir} -lgweather -lm +Cflags: -I${includedir} diff --git a/libgweather/gweather.schemas.in b/libgweather/gweather.schemas.in new file mode 100644 index 0000000..424b6f4 --- /dev/null +++ b/libgweather/gweather.schemas.in @@ -0,0 +1,173 @@ +<gconfschemafile> +<schemalist> + +<schema> + <key>/schemas/apps/gweather/prefs/auto_update</key> + <owner>gweather-applet-2</owner> + <type>bool</type> + <default>true</default> + <locale name="C"> + <short>Update the data automatically</short> + <long>Determines whether the applet automatically updates its weather statistics or not.</long> + </locale> +</schema> +<schema> + <key>/schemas/apps/gweather/prefs/auto_update_interval</key> + <owner>gweather-applet-2</owner> + <type>int</type> + <default>1800</default> + <locale name="C"> + <short>Update interval</short> + <long>The interval, in seconds, between automatic updates.</long> + </locale> +</schema> +<schema> + <key>/schemas/apps/gweather/prefs/enable_metric</key> + <owner>gweather-applet-2</owner> + <type>bool</type> + <default>false</default> + <locale name="C"> + <short>Use metric units</short> + <long>Use metric units instead of english units.</long> + </locale> +</schema> +<schema> + <key>/schemas/apps/gweather/prefs/distance_unit</key> + <owner>gweather-applet-2</owner> + <type>string</type> + <default>Default</default> + <locale name="C"> + <short>Distance unit</short> + <long>The unit to use for visibility.</long> + </locale> +</schema> +<schema> + <key>/schemas/apps/gweather/prefs/pressure_unit</key> + <owner>gweather-applet-2</owner> + <type>string</type> + <default>Default</default> + <locale name="C"> + <short>Pressure unit</short> + <long>The unit to use for pressure.</long> + </locale> +</schema> +<schema> + <key>/schemas/apps/gweather/prefs/speed_unit</key> + <owner>gweather-applet-2</owner> + <type>string</type> + <default>Default</default> + <locale name="C"> + <short>Speed unit</short> + <long>The unit to use for wind speed.</long> + </locale> +</schema> +<schema> + <key>/schemas/apps/gweather/prefs/temperature_unit</key> + <owner>gweather-applet-2</owner> + <type>string</type> + <default>Default</default> + <locale name="C"> + <short>Temperature unit</short> + <long>The unit to use for temperature.</long> + </locale> +</schema> +<schema> + <key>/schemas/apps/gweather/prefs/enable_detailed_forecast</key> + <owner>gweather-applet-2</owner> + <type>bool</type> + <default>false</default> + <locale name="C"> + <short>Not used anymore</short> + </locale> +</schema> +<schema> + <key>/schemas/apps/gweather/prefs/enable_radar_map</key> + <owner>gweather-applet-2</owner> + <type>bool</type> + <default>false</default> + <locale name="C"> + <short>Display radar map</short> + <long>Fetch a radar map on each update.</long> + </locale> +</schema> +<schema> + <key>/schemas/apps/gweather/prefs/location0</key> + <owner>gweather-applet-2</owner> + <type>string</type> + <locale name="C"> + <default>DEFAULT_LOCATION</default> + <short>Weather location information</short> + <long>Weather location information.</long> + </locale> +</schema> +<schema> + <key>/schemas/apps/gweather/prefs/location1</key> + <owner>gweather-applet-2</owner> + <type>string</type> + <locale name="C"> + <default>DEFAULT_CODE</default> + <short>Nearby city</short> + <long>Nearby major zone, such as a capital city, as found from http://cvs.gnome.org/viewcvs/*checkout*/gnome-applets/gweather/Locations.xml.in</long> + </locale> +</schema> +<schema> + <key>/schemas/apps/gweather/prefs/location2</key> + <owner>gweather-applet-2</owner> + <type>string</type> + <locale name="C"> + <default>DEFAULT_ZONE</default> + <short>Zone location</short> + <long>A unique zone for the city, as found from http://cvs.gnome.org/viewcvs/*checkout*/gnome-applets/gweather/Locations.xml.in</long> + </locale> +</schema> +<schema> + <key>/schemas/apps/gweather/prefs/location3</key> + <owner>gweather-applet-2</owner> + <type>string</type> + <locale name="C"> + <default>DEFAULT_RADAR</default> + <short>Radar location</short> + <long>A three-digit-long code for retrieving radar maps from weather.com, found from http://cvs.gnome.org/viewcvs/*checkout*/gnome-applets/gweather/Locations.xml.in</long> + </locale> +</schema> +<schema> + <key>/schemas/apps/gweather/prefs/location4</key> + <owner>gweather-applet-2</owner> + <type>string</type> + <locale name="C"> + <default>DEFAULT_LOCATION</default> + <short>Weather for a city</short> + <long>The city that gweather displays information for.</long> + </locale> +</schema> +<schema> + <key>/schemas/apps/gweather/prefs/coordinates</key> + <owner>gweather-applet-2</owner> + <type>string</type> + <locale name="C"> + <default>DEFAULT_COORDINATES</default> + <short>Location coordinates</short> + <long>Latitude and longitude of your location expressed in DD-MM-SS[NS] DD-MM-SS[EW].</long> + </locale> +</schema> +<schema> + <key>/schemas/apps/gweather/prefs/use_custom_radar_url</key> + <owner>gweather-applet-2</owner> + <type>bool</type> + <default>false</default> + <locale name="C"> + <short>Use custom url for the radar map</short> + <long>If true, then retrieve a radar map from a location specified by the "radar" key.</long> + </locale> +</schema> +<schema> + <key>/schemas/apps/gweather/prefs/radar</key> + <owner>gweather-applet-2</owner> + <type>string</type> + <locale name="C"> + <short>Url for the radar map</short> + <long>The custom url from where to retrieve a radar map.</long> + </locale> +</schema> +</schemalist> +</gconfschemafile> diff --git a/libgweather/test_metar.c b/libgweather/test_metar.c new file mode 100644 index 0000000..afa6d49 --- /dev/null +++ b/libgweather/test_metar.c @@ -0,0 +1,78 @@ +/* $Id: test_metar.c 9270 2006-01-15 10:15:19Z davyd $ */ + +/* + * Simple program to reproduce METAR parsing results from command line + */ + +#include <glib.h> +#include <string.h> +#include <stdio.h> +#include "weather-priv.h" + +#ifndef BUFLEN +#define BUFLEN 4096 +#endif /* BUFLEN */ + +int +main (int argc, char **argv) +{ + FILE* stream = stdin; + gchar* filename = NULL; + GOptionEntry entries[] = + { + { "file", 'f', 0, G_OPTION_ARG_FILENAME, &filename, + "file constaining metar observations", NULL }, + { NULL } + }; + GOptionContext* context; + GError* error = NULL; + char buf[BUFLEN]; + int len; + WeatherInfo info; + + + context = g_option_context_new ("- test libgweather metar parser"); + g_option_context_add_main_entries (context, entries, NULL); + g_option_context_parse (context, &argc, &argv, &error); + + if (error) { + perror(error->message); + return error->code; + } + if (filename) { + stream = fopen(filename, "r"); + if (!stream) { + perror("fopen"); + return -1; + } + } + else + { + fprintf (stderr, "Enter a METAR string...\n"); + } + + while (fgets(buf, sizeof(buf), stream)) { + len = strlen(buf); + if (buf[len - 1] == '\n') { + buf[--len] = '\0'; + } + printf("\n%s\n", buf); + + memset (&info, 0, sizeof(info)); + info.valid = 1; + metar_parse(buf, &info); + printf("Returned info:\n"); + printf(" update: %s", ctime(&info.update)); + printf(" sky: %s\n", weather_info_get_sky(&info)); + printf(" cond: %s\n", weather_info_get_conditions(&info)); + printf(" temp: %g F\n", info.temp); + printf(" dewp: %g F\n", info.dew); + printf(" winddir: %s\n", weather_wind_direction_string(info.wind)); + printf(" windsp: %d knots\n", info.windspeed); + printf(" pressure: %g\" Hg\n", info.pressure); + printf(" vis: %g miles\n", info.visibility); + + // TODO: retrieve location's lat/lon to display sunrise/set times + } + return 0; +} diff --git a/libgweather/weather-bom.c b/libgweather/weather-bom.c new file mode 100644 index 0000000..3bc74ca --- /dev/null +++ b/libgweather/weather-bom.c @@ -0,0 +1,125 @@ +/* $Id: weather-bom.c 9804 2006-08-07 14:05:56Z davyd $ */ + +/* + * Papadimitriou Spiros <spapadim+@cs.cmu.edu> + * + * This code released under the GNU GPL. + * Read the file COPYING for more information. + * + * Weather server functions (BOM) + * + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <string.h> +#include <libgweather/weather.h> +#include "weather-priv.h" + +static gchar *bom_parse (gchar *meto) +{ + gchar *p, *rp; + + g_return_val_if_fail (meto != NULL, NULL); + + p = strstr(meto, "<pre>"); + g_return_val_if_fail (p != NULL, NULL); + + rp = strstr(p, "</pre>"); + g_return_val_if_fail (rp !=NULL, NULL); + + p += 5; /* skip the <pre> */ + + return g_strndup(p, rp-p); +} + +static void bom_finish_read(GnomeVFSAsyncHandle *handle, GnomeVFSResult result, + gpointer buffer, GnomeVFSFileSize requested, + GnomeVFSFileSize body_len, gpointer data) +{ + WeatherInfo *info = (WeatherInfo *)data; + gchar *body, *forecast, *temp; + + g_return_if_fail(info != NULL); + g_return_if_fail(handle == info->bom_handle); + + info->forecast = NULL; + body = (gchar *)buffer; + body[body_len] = '\0'; + + if (info->bom_buffer == NULL) + info->bom_buffer = g_strdup(body); + else + { + temp = g_strdup(info->bom_buffer); + g_free(info->bom_buffer); + info->bom_buffer = g_strdup_printf("%s%s", temp, body); + g_free(temp); + } + + if (result == GNOME_VFS_ERROR_EOF) + { + forecast = bom_parse(info->bom_buffer); + info->forecast = forecast; + } + else if (result != GNOME_VFS_OK) { + info->bom_handle = NULL; + requests_done_check (info); + g_warning("Failed to get BOM data.\n"); + } else { + gnome_vfs_async_read(handle, body, DATA_SIZE - 1, bom_finish_read, info); + + return; + } + + request_done(info->bom_handle, info); + g_free (buffer); + return; +} + +static void bom_finish_open (GnomeVFSAsyncHandle *handle, GnomeVFSResult result, gpointer data) +{ + WeatherInfo *info = (WeatherInfo *)data; + WeatherLocation *loc; + gchar *body; + + g_return_if_fail(info != NULL); + g_return_if_fail(handle == info->bom_handle); + + body = g_malloc0(DATA_SIZE); + + info->bom_buffer = NULL; + if (info->forecast) + g_free (info->forecast); + info->forecast = NULL; + loc = info->location; + g_return_if_fail(loc != NULL); + + if (result != GNOME_VFS_OK) { + g_warning("Failed to get BOM forecast data.\n"); + info->bom_handle = NULL; + requests_done_check (info); + g_free (body); + } else { + gnome_vfs_async_read(handle, body, DATA_SIZE - 1, bom_finish_read, info); + } + return; +} + +void bom_start_open (WeatherInfo *info) +{ + gchar *url; + WeatherLocation *loc; + loc = info->location; + + url = g_strdup_printf("http://www.bom.gov.au/cgi-bin/wrap_fwo.pl?%s.txt", + loc->zone+1); + + gnome_vfs_async_open(&info->bom_handle, url, GNOME_VFS_OPEN_READ, + 0, bom_finish_open, info); + g_free(url); + + return; +} diff --git a/libgweather/weather-iwin.c b/libgweather/weather-iwin.c new file mode 100644 index 0000000..15f3a60 --- /dev/null +++ b/libgweather/weather-iwin.c @@ -0,0 +1,204 @@ +/* $Id: weather-iwin.c 10523 2007-11-16 08:03:22Z callum $ */ + +/* + * Papadimitriou Spiros <spapadim+@cs.cmu.edu> + * + * This code released under the GNU GPL. + * Read the file COPYING for more information. + * + * Weather server functions (IWIN) + * + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <ctype.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <regex.h> +#include <glib/gi18n-lib.h> + +#include <libgweather/weather.h> +#include "weather-priv.h" + +#define IWIN_RE_STR "([A-Z][A-Z]Z(([0-9]{3}>[0-9]{3}-)|([0-9]{3}-))+)+([0-9]{6}-)?" + +/** + * Unused. Are these functions useful? + */ + +/** + * Human's don't deal well with .MONDAY...SUNNY AND BLAH BLAH.TUESDAY...THEN THIS AND THAT.WEDNESDAY...RAINY BLAH BLAH. + * This function makes it easier to read. + */ +static gchar* formatWeatherMsg (gchar* forecast) { + + gchar* ptr = forecast; + gchar* startLine = NULL; + + while (0 != *ptr) { + if (ptr[0] == '\n' && ptr[1] == '.') { + /* This removes the preamble by shifting the relevant data + * down to the start of the buffer. */ + if (NULL == startLine) { + memmove(forecast, ptr, strlen(ptr) + 1); + ptr = forecast; + ptr[0] = ' '; + } + ptr[1] = '\n'; + ptr += 2; + startLine = ptr; + } else if (ptr[0] == '.' && ptr[1] == '.' && ptr[2] == '.' && NULL != startLine) { + memmove(startLine + 2, startLine, (ptr - startLine) * sizeof(gchar)); + startLine[0] = ' '; + startLine[1] = '\n'; + ptr[2] = '\n'; + + ptr += 3; + + } else if (ptr[0] == '$' && ptr[1] == '$') { + ptr[0] = ptr[1] = ' '; + + } else { + ptr++; + } + } + + return forecast; +} + + +static void iwin_finish_read(GnomeVFSAsyncHandle *handle, GnomeVFSResult result, + gpointer buffer, GnomeVFSFileSize requested, + GnomeVFSFileSize body_len, gpointer data) +{ + WeatherInfo *info = (WeatherInfo *)data; + gchar *body, *temp; + + g_return_if_fail(info != NULL); + g_return_if_fail(handle == info->iwin_handle); + + info->forecast = NULL; + body = (gchar *)buffer; + body[body_len] = '\0'; + + if (info->iwin_buffer == NULL) + info->iwin_buffer = g_strdup(body); + else + { + temp = g_strdup(info->iwin_buffer); + g_free(info->iwin_buffer); + info->iwin_buffer = g_strdup_printf("%s%s", temp, body); + g_free(temp); + } + + if (result == GNOME_VFS_ERROR_EOF) + { + info->forecast = formatWeatherMsg(g_strdup (info->iwin_buffer)); + } + else if (result != GNOME_VFS_OK) { + g_print("%s", gnome_vfs_result_to_string(result)); + g_warning("Failed to get IWIN data.\n"); + } else { + gnome_vfs_async_read(handle, body, DATA_SIZE - 1, iwin_finish_read, info); + return; + } + + request_done(info->iwin_handle, info); + g_free (buffer); + return; +} + +static void iwin_finish_open (GnomeVFSAsyncHandle *handle, GnomeVFSResult result, gpointer data) +{ + WeatherInfo *info = (WeatherInfo *)data; + WeatherLocation *loc; + gchar *body; + + g_return_if_fail(info != NULL); + g_return_if_fail(handle == info->iwin_handle); + + body = g_malloc0(DATA_SIZE); + + if (info->iwin_buffer) + g_free (info->iwin_buffer); + info->iwin_buffer = NULL; + if (info->forecast) + g_free (info->forecast); + info->forecast = NULL; + loc = info->location; + if (loc == NULL) { + g_warning (_("WeatherInfo missing location")); + request_done(info->iwin_handle, info); + info->iwin_handle = NULL; + requests_done_check(info); + g_free (body); + return; + } + + if (result != GNOME_VFS_OK) { + /* forecast data is not really interesting anyway ;) */ + g_warning("Failed to get IWIN forecast data.\n"); + info->iwin_handle = NULL; + requests_done_check (info); + g_free (body); + } else { + gnome_vfs_async_read(handle, body, DATA_SIZE - 1, iwin_finish_read, info); + } + return; +} + +/* Get forecast into newly alloc'ed string */ +void iwin_start_open (WeatherInfo *info) +{ + gchar *url, *state, *zone; + WeatherLocation *loc; + + g_return_if_fail(info != NULL); + loc = info->location; + g_return_if_fail(loc != NULL); + + if (loc->zone[0] == '-') + return; + + if (loc->zone[0] == ':') /* Met Office Region Names */ + { + metoffice_start_open (info); + return; + } + if (loc->zone[0] == '@') /* Australian BOM forecasts */ + { + bom_start_open (info); + return; + } + +#if 0 + if (info->forecast_type == FORECAST_ZONE) + url = g_strdup_printf("http://iwin.nws.noaa.gov/iwin/%s/zone.html", + loc->zone); + else + url = g_strdup_printf("http://iwin.nws.noaa.gov/iwin/%s/state.html", + loc->zone); +#endif + + /* The zone for Pittsburgh (for example) is given as PAZ021 in the locations + ** file (the PA stands for the state pennsylvania). The url used wants the state + ** as pa, and the zone as lower case paz021. + */ + zone = g_ascii_strdown (loc->zone, -1); + state = g_strndup (zone, 2); + + url = g_strdup_printf ("http://weather.noaa.gov/pub/data/forecasts/zone/%s/%s.txt", + state, zone); + g_free (zone); + g_free (state); + + gnome_vfs_async_open(&info->iwin_handle, url, GNOME_VFS_OPEN_READ, + 0, iwin_finish_open, info); + g_free(url); + +} + diff --git a/libgweather/weather-met.c b/libgweather/weather-met.c new file mode 100644 index 0000000..c10cede --- /dev/null +++ b/libgweather/weather-met.c @@ -0,0 +1,238 @@ +/* $Id: weather-met.c 10124 2007-01-05 14:02:46Z kmaraas $ */ + +/* + * Papadimitriou Spiros <spapadim+@cs.cmu.edu> + * + * This code released under the GNU GPL. + * Read the file COPYING for more information. + * + * Weather server functions (MET) + * + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <ctype.h> +#include <stdlib.h> +#include <string.h> +#include <strings.h> + +#include <libgweather/weather.h> +#include "weather-priv.h" + +static char *met_reprocess(char *x, int len) +{ + char *p = x; + char *o; + int spacing = 0; + static gchar *buf; + static gint buflen = 0; + gchar *lastspace = NULL; + int count = 0; + + if(buflen < len) + { + if(buf) + g_free(buf); + buf=g_malloc(len + 1); + buflen=len; + } + + o=buf; + x += len; /* End mark */ + + while(*p && p < x) + { + if(isspace(*p)) + { + if(!spacing) + { + spacing = 1; + lastspace = o; + count++; + *o++ = ' '; + } + p++; + continue; + } + spacing = 0; + if(count > 75 && lastspace) + { + count = o - lastspace - 1; + *lastspace = '\n'; + lastspace = NULL; + } + + if(*p=='&') + { + if(strncasecmp(p, "&", 5)==0) + { + *o++='&'; + count++; + p+=5; + continue; + } + if(strncasecmp(p, "<", 4)==0) + { + *o++='<'; + count++; + p+=4; + continue; + } + if(strncasecmp(p, ">", 4)==0) + { + *o++='>'; + count++; + p+=4; + continue; + } + } + if(*p=='<') + { + if(strncasecmp(p, "<BR>", 4)==0) + { + *o++='\n'; + count = 0; + } + if(strncasecmp(p, "<B>", 3)==0) + { + *o++='\n'; + *o++='\n'; + count = 0; + } + p++; + while(*p && *p != '>') + p++; + if(*p) + p++; + continue; + } + *o++=*p++; + count++; + } + *o=0; + return buf; +} + + +/* + * Parse the metoffice forecast info. + * For gnome 3.0 we want to just embed an HTML bonobo component and + * be done with this ;) + */ + +static gchar *met_parse (gchar *meto) +{ + gchar *p; + gchar *rp; + gchar *r = g_strdup("Met Office Forecast\n"); + gchar *t; + + g_return_val_if_fail (meto != NULL, r); + + p = strstr(meto, "Summary: </b>"); + g_return_val_if_fail (p != NULL, r); + + rp = strstr(p, "Text issued at:"); + g_return_val_if_fail (rp != NULL, r); + + p += 13; + /* p to rp is the text block we want but in HTML malformat */ + t = g_strconcat(r, met_reprocess(p, rp-p), NULL); + g_free(r); + + return t; +} + +static void met_finish_read(GnomeVFSAsyncHandle *handle, GnomeVFSResult result, + gpointer buffer, GnomeVFSFileSize requested, + GnomeVFSFileSize body_len, gpointer data) +{ + WeatherInfo *info = (WeatherInfo *)data; + WeatherLocation *loc; + gchar *body, *forecast, *temp; + + g_return_if_fail(info != NULL); + g_return_if_fail(handle == info->met_handle); + + info->forecast = NULL; + loc = info->location; + body = (gchar *)buffer; + body[body_len] = '\0'; + + if (info->met_buffer == NULL) + info->met_buffer = g_strdup(body); + else + { + temp = g_strdup(info->met_buffer); + g_free(info->met_buffer); + info->met_buffer = g_strdup_printf("%s%s", temp, body); + g_free(temp); + } + + if (result == GNOME_VFS_ERROR_EOF) + { + forecast = met_parse(info->met_buffer); + info->forecast = forecast; + } + else if (result != GNOME_VFS_OK) { + g_print("%s", gnome_vfs_result_to_string(result)); + info->met_handle = NULL; + requests_done_check (info); + g_warning("Failed to get Met Office data.\n"); + } else { + gnome_vfs_async_read(handle, body, DATA_SIZE - 1, met_finish_read, info); + return; + } + + request_done(info->met_handle, info); + g_free (buffer); + return; +} + +static void met_finish_open (GnomeVFSAsyncHandle *handle, GnomeVFSResult result, gpointer data) +{ + WeatherInfo *info = (WeatherInfo *)data; + WeatherLocation *loc; + gchar *body; + + g_return_if_fail(info != NULL); + g_return_if_fail(handle == info->met_handle); + + body = g_malloc0(DATA_SIZE); + + info->met_buffer = NULL; + if (info->forecast) + g_free (info->forecast); + info->forecast = NULL; + loc = info->location; + g_return_if_fail(loc != NULL); + + if (result != GNOME_VFS_OK) { + g_warning("Failed to get Met Office forecast data.\n"); + info->met_handle = NULL; + requests_done_check (info); + g_free (body); + } else { + gnome_vfs_async_read(handle, body, DATA_SIZE - 1, met_finish_read, info); + } + return; +} + +void metoffice_start_open (WeatherInfo *info) +{ + gchar *url; + WeatherLocation *loc; + loc = info->location; + + url = g_strdup_printf("http://www.metoffice.gov.uk/weather/europe/uk/%s.html", loc->zone+1); + + gnome_vfs_async_open(&info->met_handle, url, GNOME_VFS_OPEN_READ, + 0, met_finish_open, info); + g_free(url); + + return; +} + diff --git a/libgweather/weather-metar.c b/libgweather/weather-metar.c new file mode 100644 index 0000000..b386d48 --- /dev/null +++ b/libgweather/weather-metar.c @@ -0,0 +1,590 @@ +/* $Id: weather-metar.c 10286 2007-07-12 08:52:56Z callum $ */ + +/* + * Papadimitriou Spiros <spapadim+@cs.cmu.edu> + * + * This code released under the GNU GPL. + * Read the file COPYING for more information. + * + * Weather server functions (METAR) + * + */ + +/* + * Code for parsing METAR weather observations + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <regex.h> +#include <glib/gi18n-lib.h> + +#include <libgweather/weather.h> +#include "weather-priv.h" + + +enum +{ + TIME_RE, + WIND_RE, + VIS_RE, + COND_RE, + CLOUD_RE, + TEMP_RE, + PRES_RE, + + RE_NUM +}; + +/* Return time of weather report as secs since epoch UTC */ +static time_t make_time (gint utcDate, gint utcHour, gint utcMin) +{ + const time_t now = time(NULL); + struct tm tm; + + localtime_r (&now, &tm); + + /* If last reading took place just before midnight UTC on the + * first, adjust the date downward to allow for the month + * change-over. This ASSUMES that the reading won't be more than + * 24 hrs old! */ + if ((utcDate > tm.tm_mday) && (tm.tm_mday == 1)) { + tm.tm_mday = 0; /* mktime knows this is the last day of the previous + * month. */ + } else { + tm.tm_mday = utcDate; + } + tm.tm_hour = utcHour; + tm.tm_min = utcMin; + tm.tm_sec = 0; + + /* mktime() assumes value is local, not UTC. Use tm_gmtoff to compensate */ +#ifdef HAVE_TM_TM_GMOFF + return tm.tm_gmtoff + mktime(&tm); +#elif defined HAVE_TIMEZONE + return timezone + mktime(&tm); +#endif +} + +static void metar_tok_time (gchar *tokp, WeatherInfo *info) +{ + gint day, hr, min; + + sscanf(tokp, "%2u%2u%2u", &day, &hr, &min); + info->update = make_time(day, hr, min); +} + +static void metar_tok_wind (gchar *tokp, WeatherInfo *info) +{ + gchar sdir[4], sspd[4], sgust[4]; + gint dir, spd = -1; + gchar *gustp; + size_t glen; + + strncpy(sdir, tokp, 3); + sdir[3] = 0; + dir = (!strcmp(sdir, "VRB")) ? -1 : atoi(sdir); + + memset(sspd, 0, sizeof(sspd)); + glen = strspn(tokp+3, CONST_DIGITS); + strncpy(sspd, tokp+3, glen); + spd = atoi(sspd); + tokp += glen + 3; + + gustp = strchr(tokp, 'G'); + if (gustp) { + memset(sgust, 0, sizeof(sgust)); + glen = strspn(gustp+1, CONST_DIGITS); + strncpy(sgust, gustp+1, glen); + } + + if ((349 <= dir) || (dir <= 11)) + info->wind = WIND_N; + else if ((12 <= dir) && (dir <= 33)) + info->wind = WIND_NNE; + else if ((34 <= dir) && (dir <= 56)) + info->wind = WIND_NE; + else if ((57 <= dir) && (dir <= 78)) + info->wind = WIND_ENE; + else if ((79 <= dir) && (dir <= 101)) + info->wind = WIND_E; + else if ((102 <= dir) && (dir <= 123)) + info->wind = WIND_ESE; + else if ((124 <= dir) && (dir <= 146)) + info->wind = WIND_SE; + else if ((147 <= dir) && (dir <= 168)) + info->wind = WIND_SSE; + else if ((169 <= dir) && (dir <= 191)) + info->wind = WIND_S; + else if ((192 <= dir) && (dir <= 213)) + info->wind = WIND_SSW; + else if ((214 <= dir) && (dir <= 236)) + info->wind = WIND_SW; + else if ((237 <= dir) && (dir <= 258)) + info->wind = WIND_WSW; + else if ((259 <= dir) && (dir <= 281)) + info->wind = WIND_W; + else if ((282 <= dir) && (dir <= 303)) + info->wind = WIND_WNW; + else if ((304 <= dir) && (dir <= 326)) + info->wind = WIND_NW; + else if ((327 <= dir) && (dir <= 348)) + info->wind = WIND_NNW; + + info->windspeed = (WeatherWindSpeed)spd; +} + +static void metar_tok_vis (gchar *tokp, WeatherInfo *info) +{ + gchar *pfrac, *pend, *psp; + gchar sval[6]; + gint num, den, val; + + memset(sval, 0, sizeof(sval)); + + if (!strcmp(tokp,"CAVOK")) { + // "Ceiling And Visibility OK": visibility >= 10 KM + info->visibility=10000. / VISIBILITY_SM_TO_M(1.); + info->sky = SKY_CLEAR; + } + else if (0 != (pend = strstr(tokp, "SM"))) { + // US observation: field ends with "SM" + pfrac = strchr(tokp, '/'); + if (pfrac) { + if (*tokp == 'M') { + info->visibility = 0.001; + } else { + num = (*(pfrac-1) - '0'); + strncpy(sval, pfrac + 1, pend - pfrac - 1); + den = atoi(sval); + info->visibility = + ((WeatherVisibility)num / ((WeatherVisibility)den)); + + psp = strchr(tokp, ' '); + if (psp) { + *psp = '\0'; + val = atoi(tokp); + info->visibility += (WeatherVisibility)val; + } + } + } else { + strncpy(sval, tokp, pend - tokp); + val = atoi(sval); + info->visibility = (WeatherVisibility)val; + } + } else { + // International observation: NNNN(DD NNNNDD)? + // For now: use only the minimum visibility and ignore its direction + strncpy (sval, tokp, strspn(tokp, CONST_DIGITS)); + val = atoi(sval); + info->visibility = (WeatherVisibility)val / VISIBILITY_SM_TO_M(1.); + } +} + +static void metar_tok_cloud (gchar *tokp, WeatherInfo *info) +{ + gchar stype[4], salt[4]; + + strncpy(stype, tokp, 3); + stype[3] = 0; + if (strlen(tokp) == 6) { + strncpy(salt, tokp+3, 3); + salt[3] = 0; + } + + if (!strcmp(stype, "CLR")) { + info->sky = SKY_CLEAR; + } else if (!strcmp(stype, "SKC")) { + info->sky = SKY_CLEAR; + } else if (!strcmp(stype, "NSC")) { + info->sky = SKY_CLEAR; + } else if (!strcmp(stype, "BKN")) { + info->sky = SKY_BROKEN; + } else if (!strcmp(stype, "SCT")) { + info->sky = SKY_SCATTERED; + } else if (!strcmp(stype, "FEW")) { + info->sky = SKY_FEW; + } else if (!strcmp(stype, "OVC")) { + info->sky = SKY_OVERCAST; + } +} + +static void metar_tok_pres (gchar *tokp, WeatherInfo *info) +{ + if (*tokp == 'A') { + gchar sintg[3], sfract[3]; + gint intg, fract; + + strncpy(sintg, tokp+1, 2); + sintg[2] = 0; + intg = atoi(sintg); + + strncpy(sfract, tokp+3, 2); + sfract[2] = 0; + fract = atoi(sfract); + + info->pressure = (WeatherPressure)intg + (((WeatherPressure)fract)/100.0); + } else { /* *tokp == 'Q' */ + gchar spres[5]; + gint pres; + + strncpy(spres, tokp+1, 4); + spres[4] = 0; + pres = atoi(spres); + + info->pressure = PRESSURE_MBAR_TO_INCH((WeatherPressure)pres); + } +} + +static void metar_tok_temp (gchar *tokp, WeatherInfo *info) +{ + gchar *ptemp, *pdew, *psep; + + psep = strchr(tokp, '/'); + *psep = 0; + ptemp = tokp; + pdew = psep + 1; + + info->temp = (*ptemp == 'M') ? TEMP_C_TO_F(-atoi(ptemp+1)) + : TEMP_C_TO_F(atoi(ptemp)); + if (*pdew) { + info->dew = (*pdew == 'M') ? TEMP_C_TO_F(-atoi(pdew+1)) + : TEMP_C_TO_F(atoi(pdew)); + } else { + info->dew = -1000.0; + } +} + +static void metar_tok_cond (gchar *tokp, WeatherInfo *info) +{ + gchar squal[3], sphen[4]; + gchar *pphen; + + if ((strlen(tokp) > 3) && ((*tokp == '+') || (*tokp == '-'))) + ++tokp; /* FIX */ + + if ((*tokp == '+') || (*tokp == '-')) + pphen = tokp + 1; + else if (strlen(tokp) < 4) + pphen = tokp; + else + pphen = tokp + 2; + + memset(squal, 0, sizeof(squal)); + strncpy(squal, tokp, pphen - tokp); + squal[pphen - tokp] = 0; + + memset(sphen, 0, sizeof(sphen)); + strncpy(sphen, pphen, sizeof(sphen)); + sphen[sizeof(sphen)-1] = '\0'; + + /* Defaults */ + info->cond.qualifier = QUALIFIER_NONE; + info->cond.phenomenon = PHENOMENON_NONE; + info->cond.significant = FALSE; + + if (!strcmp(squal, "")) { + info->cond.qualifier = QUALIFIER_MODERATE; + } else if (!strcmp(squal, "-")) { + info->cond.qualifier = QUALIFIER_LIGHT; + } else if (!strcmp(squal, "+")) { + info->cond.qualifier = QUALIFIER_HEAVY; + } else if (!strcmp(squal, "VC")) { + info->cond.qualifier = QUALIFIER_VICINITY; + } else if (!strcmp(squal, "MI")) { + info->cond.qualifier = QUALIFIER_SHALLOW; + } else if (!strcmp(squal, "BC")) { + info->cond.qualifier = QUALIFIER_PATCHES; + } else if (!strcmp(squal, "PR")) { + info->cond.qualifier = QUALIFIER_PARTIAL; + } else if (!strcmp(squal, "TS")) { + info->cond.qualifier = QUALIFIER_THUNDERSTORM; + } else if (!strcmp(squal, "BL")) { + info->cond.qualifier = QUALIFIER_BLOWING; + } else if (!strcmp(squal, "SH")) { + info->cond.qualifier = QUALIFIER_SHOWERS; + } else if (!strcmp(squal, "DR")) { + info->cond.qualifier = QUALIFIER_DRIFTING; + } else if (!strcmp(squal, "FZ")) { + info->cond.qualifier = QUALIFIER_FREEZING; + } else { + return; + } + + if (!strcmp(sphen, "DZ")) { + info->cond.phenomenon = PHENOMENON_DRIZZLE; + } else if (!strcmp(sphen, "RA")) { + info->cond.phenomenon = PHENOMENON_RAIN; + } else if (!strcmp(sphen, "SN")) { + info->cond.phenomenon = PHENOMENON_SNOW; + } else if (!strcmp(sphen, "SG")) { + info->cond.phenomenon = PHENOMENON_SNOW_GRAINS; + } else if (!strcmp(sphen, "IC")) { + info->cond.phenomenon = PHENOMENON_ICE_CRYSTALS; + } else if (!strcmp(sphen, "PE")) { + info->cond.phenomenon = PHENOMENON_ICE_PELLETS; + } else if (!strcmp(sphen, "GR")) { + info->cond.phenomenon = PHENOMENON_HAIL; + } else if (!strcmp(sphen, "GS")) { + info->cond.phenomenon = PHENOMENON_SMALL_HAIL; + } else if (!strcmp(sphen, "UP")) { + info->cond.phenomenon = PHENOMENON_UNKNOWN_PRECIPITATION; + } else if (!strcmp(sphen, "BR")) { + info->cond.phenomenon = PHENOMENON_MIST; + } else if (!strcmp(sphen, "FG")) { + info->cond.phenomenon = PHENOMENON_FOG; + } else if (!strcmp(sphen, "FU")) { + info->cond.phenomenon = PHENOMENON_SMOKE; + } else if (!strcmp(sphen, "VA")) { + info->cond.phenomenon = PHENOMENON_VOLCANIC_ASH; + } else if (!strcmp(sphen, "SA")) { + info->cond.phenomenon = PHENOMENON_SAND; + } else if (!strcmp(sphen, "HZ")) { + info->cond.phenomenon = PHENOMENON_HAZE; + } else if (!strcmp(sphen, "PY")) { + info->cond.phenomenon = PHENOMENON_SPRAY; + } else if (!strcmp(sphen, "DU")) { + info->cond.phenomenon = PHENOMENON_DUST; + } else if (!strcmp(sphen, "SQ")) { + info->cond.phenomenon = PHENOMENON_SQUALL; + } else if (!strcmp(sphen, "SS")) { + info->cond.phenomenon = PHENOMENON_SANDSTORM; + } else if (!strcmp(sphen, "DS")) { + info->cond.phenomenon = PHENOMENON_DUSTSTORM; + } else if (!strcmp(sphen, "PO")) { + info->cond.phenomenon = PHENOMENON_DUST_WHIRLS; + } else if (!strcmp(sphen, "+FC")) { + info->cond.phenomenon = PHENOMENON_TORNADO; + } else if (!strcmp(sphen, "FC")) { + info->cond.phenomenon = PHENOMENON_FUNNEL_CLOUD; + } else { + return; + } + + if ((info->cond.qualifier != QUALIFIER_NONE) || (info->cond.phenomenon != PHENOMENON_NONE)) + info->cond.significant = TRUE; +} + +#define TIME_RE_STR "([0-9]{6})Z" +#define WIND_RE_STR "(([0-9]{3})|VRB)([0-9]?[0-9]{2})(G[0-9]?[0-9]{2})?KT" +#define VIS_RE_STR "((([0-9]?[0-9])|(M?([12] )?([1357]/1?[0-9])))SM)|" \ + "([0-9]{4}(N|NE|E|SE|S|SW|W|NW( [0-9]{4}(N|NE|E|SE|S|SW|W|NW))?)?)|" \ + "CAVOK" +#define COND_RE_STR "(-|\\+)?(VC|MI|BC|PR|TS|BL|SH|DR|FZ)?(DZ|RA|SN|SG|IC|PE|GR|GS|UP|BR|FG|FU|VA|SA|HZ|PY|DU|SQ|SS|DS|PO|\\+?FC)" +#define CLOUD_RE_STR "((CLR|BKN|SCT|FEW|OVC|SKC|NSC)([0-9]{3}|///)?(CB|TCU|///)?)" +#define TEMP_RE_STR "(M?[0-9][0-9])/(M?(//|[0-9][0-9])?)" +#define PRES_RE_STR "(A|Q)([0-9]{4})" + +/* POSIX regular expressions do not allow us to express "match whole words + * only" in a simple way, so we have to wrap them all into + * (^| )(...regex...)( |$) + */ +#define RE_PREFIX "(^| )(" +#define RE_SUFFIX ")( |$)" + +static regex_t metar_re[RE_NUM]; +static void (*metar_f[RE_NUM])(gchar *tokp, WeatherInfo *info); + +static void metar_init_re (void) +{ + static gboolean initialized = FALSE; + if (initialized) + return; + initialized = TRUE; + + regcomp(&metar_re[TIME_RE], RE_PREFIX TIME_RE_STR RE_SUFFIX, REG_EXTENDED); + regcomp(&metar_re[WIND_RE], RE_PREFIX WIND_RE_STR RE_SUFFIX, REG_EXTENDED); + regcomp(&metar_re[VIS_RE], RE_PREFIX VIS_RE_STR RE_SUFFIX, REG_EXTENDED); + regcomp(&metar_re[COND_RE], RE_PREFIX COND_RE_STR RE_SUFFIX, REG_EXTENDED); + regcomp(&metar_re[CLOUD_RE], RE_PREFIX CLOUD_RE_STR RE_SUFFIX, REG_EXTENDED); + regcomp(&metar_re[TEMP_RE], RE_PREFIX TEMP_RE_STR RE_SUFFIX, REG_EXTENDED); + regcomp(&metar_re[PRES_RE], RE_PREFIX PRES_RE_STR RE_SUFFIX, REG_EXTENDED); + + metar_f[TIME_RE] = metar_tok_time; + metar_f[WIND_RE] = metar_tok_wind; + metar_f[VIS_RE] = metar_tok_vis; + metar_f[COND_RE] = metar_tok_cond; + metar_f[CLOUD_RE] = metar_tok_cloud; + metar_f[TEMP_RE] = metar_tok_temp; + metar_f[PRES_RE] = metar_tok_pres; +} + +gboolean metar_parse (gchar *metar, WeatherInfo *info) +{ + gchar *p; + //gchar *rmk; + gint i, i2; + regmatch_t rm, rm2; + gchar *tokp; + + g_return_val_if_fail(info != NULL, FALSE); + g_return_val_if_fail(metar != NULL, FALSE); + + metar_init_re(); + + /* + * Force parsing to end at "RMK" field. This prevents a subtle + * problem when info within the remark happens to match an earlier state + * and, as a result, throws off all the remaining expression + */ + if (0 != (p = strstr(metar, " RMK "))) { + *p = '\0'; + //rmk = p+5; // uncomment this if RMK data becomes useful + } + + p = metar; + i = TIME_RE; + while (*p) { + + i2 = RE_NUM; + rm2.rm_so = strlen(p); + rm2.rm_eo = rm2.rm_so; + + for (i = 0; i < RE_NUM && rm2.rm_so > 0; i++) { + if (0 == regexec(&metar_re[i], p, 1, &rm, 0) + && rm.rm_so < rm2.rm_so) + { + i2 = i; + /* Skip leading and trailing space characters, if present. + (the regular expressions include those characters to + only get matches limited to whole words). */ + if (p[rm.rm_so] == ' ') rm.rm_so++; + if (p[rm.rm_eo-1] == ' ') rm.rm_eo--; + rm2.rm_so = rm.rm_so; + rm2.rm_eo = rm.rm_eo; + } + } + + if (i2 != RE_NUM) { + tokp = g_strndup(p+rm2.rm_so, rm2.rm_eo-rm2.rm_so); + metar_f[i2](tokp, info); + g_free (tokp); + } + + p += rm2.rm_eo; + p += strspn(p, " "); + } + return TRUE; +} + +static void metar_finish_read(GnomeVFSAsyncHandle *handle, GnomeVFSResult result, + gpointer buffer, GnomeVFSFileSize requested, + GnomeVFSFileSize body_len, gpointer data) +{ + WeatherInfo *info = (WeatherInfo *)data; + WeatherLocation *loc; + gchar *metar, *eoln, *body, *temp; + gboolean success = FALSE; + gchar *searchkey; + + g_return_if_fail(info != NULL); + g_return_if_fail(handle == info->metar_handle); + + loc = info->location; + body = (gchar *)buffer; + + body[body_len] = '\0'; + + if (info->metar_buffer == NULL) + info->metar_buffer = g_strdup(body); + else + { + temp = g_strdup(info->metar_buffer); + g_free(info->metar_buffer); + info->metar_buffer = g_strdup_printf("%s%s", temp, body); + g_free(temp); + } + + if (result == GNOME_VFS_ERROR_EOF) + { + + searchkey = g_strdup_printf("\n%s", loc->code); + + metar = strstr(info->metar_buffer, searchkey); + g_free (searchkey); + if (metar == NULL) { + success = FALSE; + } else { + metar += WEATHER_LOCATION_CODE_LEN + 2; + eoln = strchr(metar, '\n'); + if (eoln != NULL) + *eoln = 0; + success = metar_parse(metar, info); + if (eoln != NULL) + *eoln = '\n'; + } + + info->valid = success; + } + else if (result != GNOME_VFS_OK) { + g_print("%s", gnome_vfs_result_to_string(result)); + g_warning(_("Failed to get METAR data.\n")); + } else { + gnome_vfs_async_read(handle, body, DATA_SIZE - 1, metar_finish_read, info); + return; + } + + request_done(info->metar_handle, info); + g_free (buffer); + return; +} + +static void metar_finish_open (GnomeVFSAsyncHandle *handle, GnomeVFSResult result, gpointer data) +{ + WeatherInfo *info = (WeatherInfo *)data; + WeatherLocation *loc; + gchar *body; + + g_return_if_fail(info != NULL); + g_return_if_fail(handle == info->metar_handle); + + body = g_malloc0(DATA_SIZE); + + if (info->metar_buffer) + g_free (info->metar_buffer); + info->metar_buffer = NULL; + loc = info->location; + if (loc == NULL) { + g_warning (_("WeatherInfo missing location")); + request_done(info->metar_handle, info); + requests_done_check(info); + g_free (body); + return; + } + + if (result != GNOME_VFS_OK) { + g_warning(_("Failed to get METAR data.\n")); + info->metar_handle = NULL; + requests_done_check(info); + g_free (body); + } else { + gnome_vfs_async_read(handle, body, DATA_SIZE - 1, metar_finish_read, info); + } + return; +} + +/* Read current conditions and fill in info structure */ +void metar_start_open (WeatherInfo *info) +{ + gchar *url; + WeatherLocation *loc; + + g_return_if_fail(info != NULL); + info->valid = FALSE; + loc = info->location; + if (loc == NULL) { + g_warning (_("WeatherInfo missing location")); + return; + } + + url = g_strdup_printf("http://weather.noaa.gov/cgi-bin/mgetmetar.pl?cccc=%s", loc->code); + gnome_vfs_async_open(&info->metar_handle, url, GNOME_VFS_OPEN_READ, + 0, metar_finish_open, info); + g_free(url); + +} diff --git a/libgweather/weather-priv.h b/libgweather/weather-priv.h new file mode 100644 index 0000000..160ebba --- /dev/null +++ b/libgweather/weather-priv.h @@ -0,0 +1,211 @@ +/* + * Papadimitriou Spiros <spapadim+@cs.cmu.edu> + * + * This code released under the GNU GPL. + * Read the file COPYING for more information. + * + * Private header for weather server functions. + * + */ + +#ifndef __WEATHER_PRIV_H_ +#define __WEATHER_PRIV_H_ + +#include <time.h> +#include <libgnomevfs/gnome-vfs.h> + +#include "weather.h" + +/* + * Weather information. + */ + +enum _WeatherWindDirection { + WIND_VARIABLE, + WIND_N, WIND_NNE, WIND_NE, WIND_ENE, + WIND_E, WIND_ESE, WIND_SE, WIND_SSE, + WIND_S, WIND_SSW, WIND_SW, WIND_WSW, + WIND_W, WIND_WNW, WIND_NW, WIND_NNW +}; + +typedef enum _WeatherWindDirection WeatherWindDirection; + +enum _WeatherSky { + SKY_INVALID = -1, + SKY_CLEAR, + SKY_BROKEN, + SKY_SCATTERED, + SKY_FEW, + SKY_OVERCAST +}; + +typedef enum _WeatherSky WeatherSky; + +enum _WeatherConditionPhenomenon { + PHENOMENON_NONE, + + PHENOMENON_DRIZZLE, + PHENOMENON_RAIN, + PHENOMENON_SNOW, + PHENOMENON_SNOW_GRAINS, + PHENOMENON_ICE_CRYSTALS, + PHENOMENON_ICE_PELLETS, + PHENOMENON_HAIL, + PHENOMENON_SMALL_HAIL, + PHENOMENON_UNKNOWN_PRECIPITATION, + + PHENOMENON_MIST, + PHENOMENON_FOG, + PHENOMENON_SMOKE, + PHENOMENON_VOLCANIC_ASH, + PHENOMENON_SAND, + PHENOMENON_HAZE, + PHENOMENON_SPRAY, + PHENOMENON_DUST, + + PHENOMENON_SQUALL, + PHENOMENON_SANDSTORM, + PHENOMENON_DUSTSTORM, + PHENOMENON_FUNNEL_CLOUD, + PHENOMENON_TORNADO, + PHENOMENON_DUST_WHIRLS +}; + +typedef enum _WeatherConditionPhenomenon WeatherConditionPhenomenon; + +enum _WeatherConditionQualifier { + QUALIFIER_NONE, + + QUALIFIER_VICINITY, + + QUALIFIER_LIGHT, + QUALIFIER_MODERATE, + QUALIFIER_HEAVY, + QUALIFIER_SHALLOW, + QUALIFIER_PATCHES, + QUALIFIER_PARTIAL, + QUALIFIER_THUNDERSTORM, + QUALIFIER_BLOWING, + QUALIFIER_SHOWERS, + QUALIFIER_DRIFTING, + QUALIFIER_FREEZING +}; + +typedef enum _WeatherConditionQualifier WeatherConditionQualifier; + +struct _WeatherConditions { + gboolean significant; + WeatherConditionPhenomenon phenomenon; + WeatherConditionQualifier qualifier; +}; + +typedef struct _WeatherConditions WeatherConditions; + +typedef gdouble WeatherTemperature; +typedef gdouble WeatherHumidity; +typedef gint WeatherWindSpeed; +typedef gdouble WeatherPressure; +typedef gdouble WeatherVisibility; +typedef time_t WeatherUpdate; + +struct _WeatherInfo { + WeatherForecastType forecast_type; + + TempUnit temperature_unit; + SpeedUnit speed_unit; + PressureUnit pressure_unit; + DistanceUnit distance_unit; + + gboolean valid; + gboolean sunValid; + WeatherLocation *location; + WeatherUpdate update; + WeatherSky sky; + WeatherConditions cond; + WeatherTemperature temp; + WeatherTemperature dew; + WeatherWindDirection wind; + WeatherWindSpeed windspeed; + WeatherPressure pressure; + WeatherVisibility visibility; + WeatherUpdate sunrise; + WeatherUpdate sunset; + gchar *forecast; + gchar *metar_buffer; + gchar *iwin_buffer; + gchar *met_buffer; + gchar *bom_buffer; + gchar *radar_buffer; + gchar *radar_url; + GdkPixbufLoader *radar_loader; + GdkPixbufAnimation *radar; + GnomeVFSAsyncHandle *metar_handle; + GnomeVFSAsyncHandle *iwin_handle; + GnomeVFSAsyncHandle *wx_handle; + GnomeVFSAsyncHandle *met_handle; + GnomeVFSAsyncHandle *bom_handle; + gboolean requests_pending; + + WeatherInfoFunc finish_cb; + gpointer cb_data; +}; + +/* + * Enum -> string conversions. + */ + +const gchar * weather_wind_direction_string (WeatherWindDirection wind); +const gchar * weather_sky_string (WeatherSky sky); +const gchar * weather_conditions_string (WeatherConditions cond); + +/* Values common to the parsing source files */ + +#define DATA_SIZE 5000 + +#define CONST_DIGITS "0123456789" +#define CONST_ALPHABET "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + +/* Units conversions and names */ + +#define TEMP_F_TO_C(f) (((f) - 32.0) * 0.555556) +#define TEMP_F_TO_K(f) (TEMP_F_TO_C(f) + 273.15) +#define TEMP_C_TO_F(c) (((c) * 1.8) + 32.0) + +#define WINDSPEED_KNOTS_TO_KPH(knots) ((knots) * 1.851965) +#define WINDSPEED_KNOTS_TO_MPH(knots) ((knots) * 1.150779) +#define WINDSPEED_KNOTS_TO_MS(knots) ((knots) * 0.514444) +/* 1 bft ~= (1 m/s / 0.836) ^ (2/3) */ +#define WINDSPEED_KNOTS_TO_BFT(knots) (pow ((knots) * 0.615363, 0.666666)) + +#define PRESSURE_INCH_TO_KPA(inch) ((inch) * 3.386) +#define PRESSURE_INCH_TO_HPA(inch) ((inch) * 33.86) +#define PRESSURE_INCH_TO_MM(inch) ((inch) * 25.40005) +#define PRESSURE_INCH_TO_MB(inch) (PRESSURE_INCH_TO_HPA(inch)) +#define PRESSURE_INCH_TO_ATM(inch) ((inch) * 0.033421052) +#define PRESSURE_MBAR_TO_INCH(mbar) ((mbar) * 0.029533373) + +#define VISIBILITY_SM_TO_KM(sm) ((sm) * 1.609344) +#define VISIBILITY_SM_TO_M(sm) (VISIBILITY_SM_TO_KM(sm) * 1000) + +#define DEGREES_TO_RADIANS(deg) ((fmod(deg,360.) / 180.) * M_PI) +#define RADIANS_TO_DEGREES(rad) ((rad) * 180. / M_PI) +#define RADIANS_TO_HOURS(rad) ((rad) * 12. / M_PI) + +void metar_start_open (WeatherInfo *info); +void iwin_start_open (WeatherInfo *info); +void metoffice_start_open (WeatherInfo *info); +void bom_start_open (WeatherInfo *info); +void wx_start_open (WeatherInfo *info); + +gboolean metar_parse (gchar *metar, + WeatherInfo *info); + +gboolean requests_init (WeatherInfo *info); +void request_done (GnomeVFSAsyncHandle *handle, + WeatherInfo *info); +void requests_done_check (WeatherInfo *info); + +gboolean calc_sun (WeatherInfo *info); + +#endif /* __WEATHER_PRIV_H_ */ + diff --git a/libgweather/weather-sun.c b/libgweather/weather-sun.c new file mode 100644 index 0000000..527b793 --- /dev/null +++ b/libgweather/weather-sun.c @@ -0,0 +1,247 @@ +/* $Id: weather-sun.c 10286 2007-07-12 08:52:56Z callum $ */ + +/* + * Astronomy calculations for gweather + * + * Formulas from: + * "Practical Astronomy With Your Calculator" (3e), Peter Duffett-Smith + * Cambridge University Press 1988 + * + * Planetary Mean Orbit parameters from http://ssd.jpl.nasa.gov/elem_planets.html, + * converting longitudes from heliocentric to geocentric coordinates + * epoch J2000 (2000 Jan 1 12:00:00 UTC) + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#ifdef __FreeBSD__ +#include <sys/types.h> +#endif + +#include <math.h> +#include <time.h> +#include <glib.h> + +#include "weather-priv.h" + +#define EPOCH_TO_J2000(t) (t-946728000) +#define MEAN_ECLIPTIC_LONGITUDE 280.46435 +#define PERIGEE_LONGITUDE 282.94719 +#define ECCENTRICITY 0.01671002 +#define MEAN_ECLIPTIC_OBLIQUITY +#define SOL_PROGRESSION (360./365.242191) + +/* + * Convert ecliptic longitude (radians) to equitorial coordinates, + * expressed as right ascension (hours) and declination (radians) + * Assume ecliptic latitude is 0.0 + */ +static void ecl2equ (gdouble time, gdouble eclipLon, + gdouble *ra, gdouble *decl) +{ + gdouble jc = EPOCH_TO_J2000(time) / (36525. * 86400.); + gdouble mEclipObliq = DEGREES_TO_RADIANS(23.439291667 + - .013004166 * jc + - 1.666667e-7 * jc * jc + + 5.027777e-7 * jc * jc * jc); + *ra = RADIANS_TO_HOURS(atan2 ( sin(eclipLon) * cos(mEclipObliq), cos(eclipLon))); + if (*ra < 0.) + *ra += 24.; + *decl = asin ( sin(mEclipObliq) * sin(eclipLon) ); +} + +/* + * Calculate rising and setting times of an object + * based on it equitorial coordinates + */ +static void gstObsv (gdouble ra, gdouble decl, + gdouble obsLat, gdouble obsLon, + gdouble *rise, gdouble *set) +{ + double a = acos(-tan(obsLat) * tan(decl)); + double b; + + if (isnan(a) != 0) { + *set = *rise = a; + return; + } + a = RADIANS_TO_HOURS(a); + b = 24. - a + ra; + a += ra; + a -= RADIANS_TO_HOURS(obsLon); + b -= RADIANS_TO_HOURS(obsLon); + if ((a = fmod(a, 24.)) < 0) + a += 24.; + if ((b = fmod(b, 24.)) < 0) + b += 24.; + + *set = a; + *rise = b; +} + + +static gdouble t0(time_t date) +{ + register gdouble t = ((gdouble)(EPOCH_TO_J2000(date) / 86400)) / 36525.0; + gdouble t0 = fmod( 6.697374558 + 2400.051366 * t + 2.5862e-5 * t * t, 24.); + if (t0 < 0.) + t0 += 24.; + return t0; +} + + + +static gboolean calc_sun2 (time_t t, gdouble obsLat, gdouble obsLon, + time_t *rise, time_t *set) +{ + time_t gm_midn; + time_t lcl_midn; + gdouble gm_hoff, ndays, meanAnom, eccenAnom, delta, lambda; + gdouble ra1, ra2, decl1, decl2; + gdouble rise1, rise2, set1, set2; + gdouble tt, t00; + gdouble x, u, dt; + + /* Approximate preceding local midnight at observer's longitude */ + gm_midn = t - (t % 86400); + gm_hoff = floor((RADIANS_TO_DEGREES(obsLon) + 7.5) / 15.); + lcl_midn = gm_midn - 3600. * gm_hoff; + if (t - lcl_midn >= 86400) + lcl_midn += 86400; + else if (lcl_midn > t) + lcl_midn -= 86400; + + /* + * Ecliptic longitude of the sun at midnight local time + * Start with an estimate based on a fixed daily rate + */ + ndays = EPOCH_TO_J2000(lcl_midn) / 86400.; + meanAnom = DEGREES_TO_RADIANS(ndays * SOL_PROGRESSION + + MEAN_ECLIPTIC_LONGITUDE - PERIGEE_LONGITUDE); + + /* + * Approximate solution of Kepler's equation: + * Find E which satisfies E - e sin(E) = M (mean anomaly) + */ + eccenAnom = meanAnom; + + while (1e-12 < fabs( delta = eccenAnom - ECCENTRICITY * sin(eccenAnom) - meanAnom)) + { + eccenAnom -= delta / (1.- ECCENTRICITY * cos(eccenAnom)); + } + + /* Sun's longitude on the ecliptic */ + lambda = fmod( DEGREES_TO_RADIANS ( PERIGEE_LONGITUDE ) + + 2. * atan( sqrt((1.+ECCENTRICITY)/(1.-ECCENTRICITY)) + * tan ( eccenAnom / 2. ) ), + 2. * M_PI); + + /* Calculate equitorial coordinates of sun at previous and next local midnights */ + + ecl2equ (lcl_midn, lambda, &ra1, &decl1); + ecl2equ (lcl_midn + 86400., lambda + DEGREES_TO_RADIANS(SOL_PROGRESSION), &ra2, &decl2); + + /* Convert to rise and set times assuming the earth were to stay in its position + * in its orbit around the sun. This will soon be followed by interpolating + * between the two + */ + + gstObsv (ra1, decl1, obsLat, obsLon - (gm_hoff * M_PI / 12.), &rise1, &set1); + gstObsv (ra2, decl2, obsLat, obsLon - (gm_hoff * M_PI / 12.), &rise2, &set2); + + /* TODO: include calculations for regions near the poles. */ + if (isnan(rise1) || isnan(rise2)) + return FALSE; + + if (rise2 < rise1) { + rise2 += 24.; + } + if (set2 < set1) { + set2 += 24.; + } + + tt = t0(lcl_midn); + t00 = tt - (gm_hoff + RADIANS_TO_HOURS(obsLon)) * 1.002737909; + + if (t00 < 0.) + t00 += 24.; + + if (rise1 < t00) { + rise1 += 24.; + rise2 += 24.; + } + if (set1 < t00) { + set1 += 24.; + set2 += 24.; + } + + /* + * Interpolate between the two + */ + rise1 = (24.07 * rise1 - t00 * (rise2 - rise1)) / (24.07 + rise1 - rise2); + set1 = (24.07 * set1 - t00 * (set2 - set1)) / (24.07 + set1 - set2); + + decl2 = (decl1 + decl2) / 2.; + x = DEGREES_TO_RADIANS(0.830725); + u = acos ( sin(obsLat) / cos(decl2) ); + dt = RADIANS_TO_HOURS ( asin ( sin(x) / sin(u) ) + / cos(decl2) ); + + rise1 = (rise1 - dt - tt) * 0.9972695661; + set1 = (set1 + dt - tt) * 0.9972695661; + if (rise1 < 0.) + rise1 += 24; + else if (rise1 >= 24.) + rise1 -= 24.; + if (set1 < 0.) + set1 += 24; + else if (set1 >= 24.) + set1 -= 24.; + + *rise = (rise1 * 3600.) + lcl_midn; + *set = (set1 * 3600.) + lcl_midn; + return TRUE; +} + + +gboolean calc_sun (WeatherInfo *info) +{ + time_t now = time (NULL); + + return info->location->latlon_valid + && calc_sun2(now, info->location->latitude, info->location->longitude, + &info->sunrise, &info->sunset); +} + + +/* + * Returns the interval, in seconds, until the next "sun event": + * - local midnight, when rise and set times are recomputed + * - next sunrise, when icon changes to daytime version + * - next sunset, when icon changes to nighttime version + */ +gint weather_info_next_sun_event(WeatherInfo *info) +{ + time_t now = time(NULL); + struct tm ltm; + time_t nxtEvent; + + if (!calc_sun(info)) + return -1; + + /* Determine when the next local midnight occurs */ + (void) localtime_r (&now, <m); + ltm.tm_sec = 0; + ltm.tm_min = 0; + ltm.tm_hour = 0; + ltm.tm_mday++; + nxtEvent = mktime(<m); + + if (info->sunset > now && info->sunset < nxtEvent) + nxtEvent = info->sunset; + if (info->sunrise > now && info->sunrise < nxtEvent) + nxtEvent = info->sunrise; + return (gint)(nxtEvent - now); +} diff --git a/libgweather/weather-wx.c b/libgweather/weather-wx.c new file mode 100644 index 0000000..64d2ad3 --- /dev/null +++ b/libgweather/weather-wx.c @@ -0,0 +1,128 @@ +/* $Id: weather-wx.c 9147 2005-12-12 00:22:17Z philipl $ */ + +/* + * Papadimitriou Spiros <spapadim+@cs.cmu.edu> + * + * This code released under the GNU GPL. + * Read the file COPYING for more information. + * + * Weather server functions (WX Radar) + * + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <glib/gi18n-lib.h> + +#include <libgweather/weather.h> +#include "weather-priv.h" + +static void wx_finish_read(GnomeVFSAsyncHandle *handle, GnomeVFSResult result, + gpointer buffer, GnomeVFSFileSize requested, + GnomeVFSFileSize body_len, gpointer data) +{ + WeatherInfo *info = (WeatherInfo *)data; + + g_return_if_fail(info != NULL); + g_return_if_fail(handle == info->wx_handle); + + info->radar = NULL; + + if (result == GNOME_VFS_OK && body_len != 0) { + GError *error = NULL; + gdk_pixbuf_loader_write (info->radar_loader, buffer, body_len, &error); + if (error) { + g_print ("%s \n", error->message); + g_error_free (error); + } + gnome_vfs_async_read(handle, buffer, DATA_SIZE - 1, wx_finish_read, info); + return; + } + else if (result == GNOME_VFS_ERROR_EOF) + { + GdkPixbufAnimation *animation; + + gdk_pixbuf_loader_close (info->radar_loader, NULL); + animation = gdk_pixbuf_loader_get_animation (info->radar_loader); + if (animation != NULL) + { + if (info->radar) + g_object_unref (info->radar); + info->radar = animation; + g_object_ref (info->radar); + } + g_object_unref (G_OBJECT (info->radar_loader)); + + } + else + { + g_print("%s", gnome_vfs_result_to_string(result)); + g_warning(_("Failed to get METAR data.\n")); + info->wx_handle = NULL; + requests_done_check (info); + if(info->radar_loader) g_object_unref (G_OBJECT (info->radar_loader)); + } + + request_done(info->wx_handle, info); + g_free (buffer); + return; +} + +static void wx_finish_open (GnomeVFSAsyncHandle *handle, GnomeVFSResult result, gpointer data) +{ + WeatherInfo *info = (WeatherInfo *)data; + WeatherLocation *loc; + gchar *body; + + g_return_if_fail(info != NULL); + g_return_if_fail(handle == info->wx_handle); + + body = g_malloc0(DATA_SIZE); + + info->radar_buffer = NULL; + info->radar = NULL; + loc = info->location; + g_return_if_fail(loc != NULL); + + if (result != GNOME_VFS_OK) { + g_warning("Failed to get radar map image.\n"); + info->wx_handle = NULL; + requests_done_check (info); + g_free (body); + } else { + gnome_vfs_async_read(handle, body, DATA_SIZE -1, wx_finish_read, info); + + } + return; +} + +/* Get radar map and into newly allocated pixmap */ +void wx_start_open (WeatherInfo *info) +{ + gchar *url; + WeatherLocation *loc; + + g_return_if_fail(info != NULL); + info->radar = NULL; + info->radar_loader = gdk_pixbuf_loader_new (); + loc = info->location; + g_return_if_fail(loc != NULL); + + + if (info->radar_url) + url = g_strdup (info->radar_url); + else { + if (loc->radar[0] == '-') return; + url = g_strdup_printf ("http://image.weather.com/web/radar/us_%s_closeradar_medium_usen.jpg", loc->radar); + } + + gnome_vfs_async_open(&info->wx_handle, url, GNOME_VFS_OPEN_READ, + 0, wx_finish_open, info); + + g_free(url); + + return; +} + diff --git a/libgweather/weather.c b/libgweather/weather.c new file mode 100644 index 0000000..2120014 --- /dev/null +++ b/libgweather/weather.c @@ -0,0 +1,1090 @@ +/* $Id: weather.c 10520 2007-11-14 21:49:57Z ryanl $ */ + +/* + * Papadimitriou Spiros <spapadim+@cs.cmu.edu> + * + * This code released under the GNU GPL. + * Read the file COPYING for more information. + * + * Overall weather server functions. + * + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <assert.h> +#include <string.h> +#include <ctype.h> +#include <math.h> + +#ifdef HAVE_VALUES_H +#include <values.h> +#endif + +#ifdef __FreeBSD__ +#include <sys/types.h> +#endif +#include <regex.h> +#include <time.h> +#include <unistd.h> + +#include <glib/gi18n-lib.h> +#include <gtk/gtkicontheme.h> +#include <gdk-pixbuf/gdk-pixbuf.h> +#include <gdk-pixbuf/gdk-pixbuf-loader.h> +#include <libgnomevfs/gnome-vfs.h> + +#include <libgweather/weather.h> +#include "weather-priv.h" + +void +close_cb (GnomeVFSAsyncHandle *handle, GnomeVFSResult result, gpointer data); + + +/* + * Convert string of the form "DD-MM-SSH" to radians + * DD:degrees (to 3 digits), MM:minutes, SS:seconds H:hemisphere (NESW) + * Return value is positive for N,E; negative for S,W. + */ +static gdouble dmsh2rad (const gchar *latlon) +{ + char *p1, *p2; + int deg, min, sec, dir; + gdouble value; + + if (latlon == NULL) + return DBL_MAX; + p1 = strchr(latlon, '-'); + p2 = strrchr(latlon, '-'); + if (p1 == NULL || p1 == latlon) { + return DBL_MAX; + } else if (p1 == p2) { + sscanf (latlon, "%d-%d", °, &min); + sec = 0; + } else if (p2 == 1 + p1) { + return DBL_MAX; + } else { + sscanf (latlon, "%d-%d-%d", °, &min, &sec); + } + if (deg > 180 || min >= 60 || sec >= 60) + return DBL_MAX; + value = (gdouble)((deg * 60 + min) * 60 + sec) * M_PI / 648000.; + + dir = toupper(latlon[strlen(latlon) - 1]); + if (dir == 'W' || dir == 'S') + value = -value; + else if (dir != 'E' && dir != 'N' && (value != 0.0 || dir != '0')) + value = DBL_MAX; + return value; +} + +WeatherLocation *weather_location_new (const gchar *name, const gchar *code, + const gchar *zone, const gchar *radar, + const gchar *coordinates) +{ + WeatherLocation *location; + + location = g_new(WeatherLocation, 1); + + /* name and metar code must be set */ + location->name = g_strdup(name); + location->code = g_strdup(code); + + if (zone) { + location->zone = g_strdup(zone); + } else { + location->zone = g_strdup("------"); + } + + if (radar) { + location->radar = g_strdup(radar); + } else { + location->radar = g_strdup("---"); + } + + if (location->zone[0] == '-') { + location->zone_valid = FALSE; + } else { + location->zone_valid = TRUE; + } + + location->coordinates = NULL; + if (coordinates) + { + char **pieces; + + pieces = g_strsplit (coordinates, " ", -1); + + if (g_strv_length (pieces) == 2) + { + location->coordinates = g_strdup(coordinates); + location->latitude = dmsh2rad (pieces[0]); + location->longitude = dmsh2rad (pieces[1]); + } + + g_strfreev (pieces); + } + + if (!location->coordinates) + { + location->coordinates = g_strdup("---"); + location->latitude = DBL_MAX; + location->longitude = DBL_MAX; + } + + location->latlon_valid = (location->latitude < DBL_MAX && location->longitude < DBL_MAX); + + return location; +} + +WeatherLocation *weather_location_clone (const WeatherLocation *location) +{ + WeatherLocation *clone; + + clone = weather_location_new (location->name, + location->code, location->zone, + location->radar, location->coordinates); + clone->latitude = location->latitude; + clone->longitude = location->longitude; + clone->latlon_valid = location->latlon_valid; + return clone; +} + +void weather_location_free (WeatherLocation *location) +{ + if (location) { + g_free (location->name); + g_free (location->code); + g_free (location->zone); + g_free (location->radar); + g_free (location->coordinates); + + g_free (location); + } +} + +gboolean weather_location_equal (const WeatherLocation *location1, const WeatherLocation *location2) +{ + if (!location1->code || !location2->code) + return 1; + return ( (strcmp(location1->code, location2->code) == 0) && + (strcmp(location1->name, location2->name) == 0) ); +} + +static const gchar *wind_direction_str[] = { + N_("Variable"), + N_("North"), N_("North - NorthEast"), N_("Northeast"), N_("East - NorthEast"), + N_("East"), N_("East - Southeast"), N_("Southeast"), N_("South - Southeast"), + N_("South"), N_("South - Southwest"), N_("Southwest"), N_("West - Southwest"), + N_("West"), N_("West - Northwest"), N_("Northwest"), N_("North - Northwest") +}; + +const gchar *weather_wind_direction_string (WeatherWindDirection wind) +{ + if (wind < 0) + return _("Unknown"); + if (wind >= (sizeof (wind_direction_str) / sizeof (char *))) + return _("Invalid"); + + return _(wind_direction_str[(int)wind]); +} + +static const gchar *sky_str[] = { + N_("Clear Sky"), + N_("Broken clouds"), + N_("Scattered clouds"), + N_("Few clouds"), + N_("Overcast") +}; + +const gchar *weather_sky_string (WeatherSky sky) +{ + if (sky < 0 || + sky >= (sizeof (sky_str) / sizeof (char *))) + return _("Invalid"); + + return _(sky_str[(int)sky]); +} + + +/* + * Even though tedious, I switched to a 2D array for weather condition + * strings, in order to facilitate internationalization, esp. for languages + * with genders. + */ + +/* + * Almost all reportable combinations listed in + * http://www.crh.noaa.gov/arx/wx.tbl.html are entered below, except those + * having 2 qualifiers mixed together [such as "Blowing snow in vicinity" + * (VCBLSN), "Thunderstorm in vicinity" (VCTS), etc]. + * Combinations that are not possible are filled in with "??". + * Some other exceptions not handled yet, such as "SN BLSN" which has + * special meaning. + */ + +/* + * Note, magic numbers, when you change the size here, make sure to change + * the below function so that new values are recognized + */ +/* NONE VICINITY LIGHT MODERATE HEAVY SHALLOW PATCHES PARTIAL THUNDERSTORM BLOWING SHOWERS DRIFTING FREEZING */ +/* *******************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************/ +static const gchar *conditions_str[24][13] = { +/* TRANSLATOR: If you want to know what "blowing" "shallow" "partial" + * etc means, you can go to http://www.weather.com/glossary/ and + * http://www.crh.noaa.gov/arx/wx.tbl.html */ +/* NONE */ {"??", "??", "??", "??", "??", "??", "??", "??", N_("Thunderstorm"), "??", "??", "??", "??" }, +/* DRIZZLE */ {N_("Drizzle"), "??", N_("Light drizzle"), N_("Moderate drizzle"), N_("Heavy drizzle"), "??", "??", "??", "??", "??", "??", "??", N_("Freezing drizzle") }, +/* RAIN */ {N_("Rain"), "??", N_("Light rain"), N_("Moderate rain"), N_("Heavy rain"), "??", "??", "??", N_("Thunderstorm"), "??", N_("Rain showers"), "??", N_("Freezing rain") }, +/* SNOW */ {N_("Snow"), "??", N_("Light snow"), N_("Moderate snow"), N_("Heavy snow"), "??", "??", "??", N_("Snowstorm"), N_("Blowing snowfall"), N_("Snow showers"), N_("Drifting snow"), "??" }, +/* SNOW_GRAINS */ {N_("Snow grains"), "??", N_("Light snow grains"), N_("Moderate snow grains"), N_("Heavy snow grains"), "??", "??", "??", "??", "??", "??", "??", "??" }, +/* ICE_CRYSTALS */ {N_("Ice crystals"), "??", "??", N_("Ice crystals"), "??", "??", "??", "??", "??", "??", "??", "??", "??" }, +/* ICE_PELLETS */ {N_("Ice pellets"), "??", N_("Few ice pellets"), N_("Moderate ice pellets"), N_("Heavy ice pellets"), "??", "??", "??", N_("Ice pellet storm"), "??", N_("Showers of ice pellets"), "??", "??" }, +/* HAIL */ {N_("Hail"), "??", "??", N_("Hail"), "??", "??", "??", "??", N_("Hailstorm"), "??", N_("Hail showers"), "??", "??", }, +/* SMALL_HAIL */ {N_("Small hail"), "??", "??", N_("Small hail"), "??", "??", "??", "??", N_("Small hailstorm"), "??", N_("Showers of small hail"), "??", "??" }, +/* PRECIPITATION */ {N_("Unknown precipitation"), "??", "??", "??", "??", "??", "??", "??", "??", "??", "??", "??", "??" }, +/* MIST */ {N_("Mist"), "??", "??", N_("Mist"), "??", "??", "??", "??", "??", "??", "??", "??", "??" }, +/* FOG */ {N_("Fog"), N_("Fog in the vicinity") , "??", N_("Fog"), "??", N_("Shallow fog"), N_("Patches of fog"), N_("Partial fog"), "??", "??", "??", "??", N_("Freezing fog") }, +/* SMOKE */ {N_("Smoke"), "??", "??", N_("Smoke"), "??", "??", "??", "??", "??", "??", "??", "??", "??" }, +/* VOLCANIC_ASH */ {N_("Volcanic ash"), "??", "??", N_("Volcanic ash"), "??", "??", "??", "??", "??", "??", "??", "??", "??" }, +/* SAND */ {N_("Sand"), "??", "??", N_("Sand"), "??", "??", "??", "??", "??", N_("Blowing sand"), "", N_("Drifting sand"), "??" }, +/* HAZE */ {N_("Haze"), "??", "??", N_("Haze"), "??", "??", "??", "??", "??", "??", "??", "??", "??" }, +/* SPRAY */ {"??", "??", "??", "??", "??", "??", "??", "??", "??", N_("Blowing sprays"), "??", "??", "??" }, +/* DUST */ {N_("Dust"), "??", "??", N_("Dust"), "??", "??", "??", "??", "??", N_("Blowing dust"), "??", N_("Drifting dust"), "??" }, +/* SQUALL */ {N_("Squall"), "??", "??", N_("Squall"), "??", "??", "??", "??", "??", "??", "??", "??", "??" }, +/* SANDSTORM */ {N_("Sandstorm"), N_("Sandstorm in the vicinity") , "??", N_("Sandstorm"), N_("Heavy sandstorm"), "??", "??", "??", "??", "??", "??", "??", "??" }, +/* DUSTSTORM */ {N_("Duststorm"), N_("Duststorm in the vicinity") , "??", N_("Duststorm"), N_("Heavy duststorm"), "??", "??", "??", "??", "??", "??", "??", "??" }, +/* FUNNEL_CLOUD */ {N_("Funnel cloud"), "??", "??", "??", "??", "??", "??", "??", "??", "??", "??", "??", "??" }, +/* TORNADO */ {N_("Tornado"), "??", "??", "??", "??", "??", "??", "??", "??", "??", "??", "??", "??" }, +/* DUST_WHIRLS */ {N_("Dust whirls"), N_("Dust whirls in the vicinity") , "??", N_("Dust whirls"), "??", "??", "??", "??", "??", "??", "??", "??", "??" } +}; + +const gchar *weather_conditions_string (WeatherConditions cond) +{ + const gchar *str; + + if (!cond.significant) { + return "-"; + } else { + if (cond.phenomenon >= 0 && + cond.phenomenon < 24 && + cond.qualifier >= 0 && + cond.qualifier < 13) + str = _(conditions_str[(int)cond.phenomenon][(int)cond.qualifier]); + else + str = _("Invalid"); + return (strlen(str) > 0) ? str : "-"; + } +} + +/* Locals turned global to facilitate asynchronous HTTP requests */ + + +gboolean requests_init (WeatherInfo *info) +{ + if (info->requests_pending) + return FALSE; + + /*g_assert(!metar_handle && !iwin_handle && !wx_handle && !met_handle);*/ + + info->requests_pending = TRUE; + + return TRUE; +} + +void request_done (GnomeVFSAsyncHandle *handle, WeatherInfo *info) +{ + if (!handle) + return; + + gnome_vfs_async_close(handle, close_cb, info); + + info->sunValid = info->valid && calc_sun(info); + return; +} + +void requests_done_check (WeatherInfo *info) +{ + g_return_if_fail(info->requests_pending); + + if (!info->metar_handle && !info->iwin_handle && + !info->wx_handle && !info->met_handle && + !info->bom_handle) { + info->requests_pending = FALSE; + info->finish_cb(info, info->cb_data); + } +} + +void +close_cb (GnomeVFSAsyncHandle *handle, GnomeVFSResult result, gpointer data) +{ + WeatherInfo *info = (WeatherInfo *)data; + + g_return_if_fail (info != NULL); + + if (result != GNOME_VFS_OK) + g_warning("Error closing GnomeVFSAsyncHandle.\n"); + + if (handle == info->metar_handle) + info->metar_handle = NULL; + if (handle == info->iwin_handle) + info->iwin_handle = NULL; + if (handle == info->wx_handle) + info->wx_handle = NULL; + if (handle == info->met_handle) + info->met_handle = NULL; + if (handle == info->bom_handle) + info->bom_handle = NULL; + + requests_done_check(info); + + return; +} + +/* Relative humidity computation - thanks to <Olof.Oberg@modopaper.modogroup.com> */ + + +static inline gdouble calc_humidity(gdouble temp, gdouble dewp) +{ + gdouble esat, esurf; + + if (temp > -500.0 && dewp > -500.0) { + temp = TEMP_F_TO_C(temp); + dewp = TEMP_F_TO_C(dewp); + + esat = 6.11 * pow(10.0, (7.5 * temp) / (237.7 + temp)); + esurf = 6.11 * pow(10.0, (7.5 * dewp) / (237.7 + dewp)); + } else { + esurf = -1.0; + esat = 1.0; + } + return ((esurf/esat) * 100.0); +} + +static inline gdouble calc_apparent (WeatherInfo *info) +{ + gdouble temp = info->temp; + gdouble wind = WINDSPEED_KNOTS_TO_MPH(info->windspeed); + gdouble apparent = -1000.; + + + /* + * Wind chill calculations as of 01-Nov-2001 + * http://www.nws.noaa.gov/om/windchill/index.shtml + * Some pages suggest that the formula will soon be adjusted + * to account for solar radiation (bright sun vs cloudy sky) + */ + if (temp <= 50.0) { + if (wind > 3.0) { + gdouble v = pow(wind, 0.16); + apparent = 35.74 + 0.6215 * temp - 35.75 * v + 0.4275 * temp * v; + } else if (wind >= 0.) { + apparent = temp; + } + } + /* + * Heat index calculations: + * http://www.srh.noaa.gov/fwd/heatindex/heat5.html + */ + else if (temp >= 80.0) { + if (info->temp >= -500. && info->dew >= -500.) { + gdouble humidity = calc_humidity(info->temp, info->dew); + gdouble t2 = temp * temp; + gdouble h2 = humidity * humidity; + +#if 1 + /* + * A really precise formula. Note that overall precision is + * constrained by the accuracy of the instruments and that the + * we receive the temperature and dewpoints as integers. + */ + gdouble t3 = t2 * temp; + gdouble h3 = h2 * temp; + + apparent = 16.923 + + 0.185212 * temp + + 5.37941 * humidity + - 0.100254 * temp * humidity + + 9.41695e-3 * t2 + + 7.28898e-3 * h2 + + 3.45372e-4 * t2 * humidity + - 8.14971e-4 * temp * h2 + + 1.02102e-5 * t2 * h2 + - 3.8646e-5 * t3 + + 2.91583e-5 * h3 + + 1.42721e-6 * t3 * humidity + + 1.97483e-7 * temp * h3 + - 2.18429e-8 * t3 * h2 + + 8.43296e-10 * t2 * h3 + - 4.81975e-11 * t3 * h3; +#else + /* + * An often cited alternative: values are within 5 degrees for + * most ranges between 10% and 70% humidity and to 110 degrees. + */ + apparent = - 42.379 + + 2.04901523 * temp + + 10.14333127 * humidity + - 0.22475541 * temp * humidity + - 6.83783e-3 * t2 + - 5.481717e-2 * h2 + + 1.22874e-3 * t2 * humidity + + 8.5282e-4 * temp * h2 + - 1.99e-6 * t2 * h2; +#endif + } + } + else { + apparent = temp; + } + + return apparent; +} + + +WeatherInfo * +_weather_info_fill (WeatherInfo *info, + WeatherLocation *location, + const WeatherPrefs *prefs, + WeatherInfoFunc cb, + gpointer data) +{ + g_return_val_if_fail(((info == NULL) && (location != NULL)) || \ + ((info != NULL) && (location == NULL)), NULL); + g_return_val_if_fail(prefs != NULL, NULL); + + /* FIXME: i'm not sure this works as intended anymore */ + if (!info) { + info = g_new0(WeatherInfo, 1); + info->metar_handle = NULL; + info->iwin_handle = NULL; + info->wx_handle = NULL; + info->met_handle = NULL; + info->bom_handle = NULL; + info->requests_pending = FALSE; + info->metar_buffer = NULL; + info->iwin_buffer = NULL; + info->met_buffer = NULL; + info->bom_buffer = NULL; + info->location = weather_location_clone(location); + } else { + location = info->location; + if (info->forecast) + g_free (info->forecast); + + info->forecast = NULL; + if (info->radar != NULL) { + g_object_unref (info->radar); + info->radar = NULL; + } + } + + /* Update in progress */ + if (!requests_init(info)) { + return NULL; + } + + /* Defaults (just in case...) */ + /* Well, no just in case anymore. We may actually fail to fetch some + * fields. */ + info->forecast_type = prefs->type; + + info->temperature_unit = prefs->temperature_unit; + info->speed_unit = prefs->speed_unit; + info->pressure_unit = prefs->pressure_unit; + info->distance_unit = prefs->distance_unit; + + info->update = 0; + info->sky = -1; + info->cond.significant = FALSE; + info->cond.phenomenon = PHENOMENON_NONE; + info->cond.qualifier = QUALIFIER_NONE; + info->temp = -1000.0; + info->dew = -1000.0; + info->wind = -1; + info->windspeed = -1; + info->pressure = -1.0; + info->visibility = -1.0; + info->sunValid = FALSE; + info->sunrise = 0; + info->sunset = 0; + info->forecast = NULL; + info->radar = NULL; + info->radar_url = prefs->radar && prefs->radar_custom_url ? + g_strdup (prefs->radar_custom_url) : NULL; + info->metar_handle = NULL; + info->iwin_handle = NULL; + info->wx_handle = NULL; + info->met_handle = NULL; + info->bom_handle = NULL; + info->requests_pending = TRUE; + info->finish_cb = cb; + info->cb_data = data; + + metar_start_open(info); + iwin_start_open(info); + + if (prefs->radar) + wx_start_open(info); + + return info; +} + +void weather_info_abort (WeatherInfo *info) +{ + if (info->metar_handle) { + gnome_vfs_async_cancel(info->metar_handle); + info->metar_handle = NULL; + } + + if (info->iwin_handle) { + gnome_vfs_async_cancel(info->iwin_handle); + info->iwin_handle = NULL; + } + + if (info->wx_handle) { + gnome_vfs_async_cancel(info->wx_handle); + info->wx_handle = NULL; + } + + if (info->met_handle) { + gnome_vfs_async_cancel(info->met_handle); + info->met_handle = NULL; + } + + if (info->bom_handle) { + gnome_vfs_async_cancel(info->bom_handle); + info->bom_handle = NULL; + } + + info->requests_pending = FALSE; +} + +WeatherInfo *weather_info_clone (const WeatherInfo *info) +{ + WeatherInfo *clone; + + g_return_val_if_fail(info != NULL, NULL); + + clone = g_new(WeatherInfo, 1); + + + /* move everything */ + g_memmove(clone, info, sizeof(WeatherInfo)); + + + /* special moves */ + clone->location = weather_location_clone(info->location); + /* This handles null correctly */ + clone->forecast = g_strdup(info->forecast); + clone->radar_url = g_strdup (info->radar_url); + + clone->radar = info->radar; + if (clone->radar != NULL) + g_object_ref (clone->radar); + + return clone; +} + +void weather_info_free (WeatherInfo *info) +{ + if (!info) + return; + + weather_info_abort (info); + + weather_location_free(info->location); + info->location = NULL; + + g_free(info->forecast); + info->forecast = NULL; + + if (info->radar != NULL) { + g_object_unref (info->radar); + info->radar = NULL; + } + + if (info->iwin_buffer) + g_free (info->iwin_buffer); + + if (info->metar_buffer) + g_free (info->metar_buffer); + + if (info->met_buffer) + g_free (info->met_buffer); + + if (info->bom_buffer) + g_free (info->bom_buffer); + + g_free(info); +} + +gboolean weather_info_is_valid (WeatherInfo *info) +{ + g_return_val_if_fail(info != NULL, FALSE); + return info->valid; +} + +const WeatherLocation *weather_info_get_location (WeatherInfo *info) +{ + g_return_val_if_fail(info != NULL, NULL); + return info->location; +} + +const gchar *weather_info_get_location_name (WeatherInfo *info) +{ + g_return_val_if_fail(info != NULL, NULL); + g_return_val_if_fail(info->location != NULL, NULL); + return info->location->name; +} + +const gchar *weather_info_get_update (WeatherInfo *info) +{ + static gchar buf[200]; + char *utf8, *timeformat; + + g_return_val_if_fail(info != NULL, NULL); + + if (!info->valid) + return "-"; + + if (info->update != 0) { + struct tm tm; + localtime_r (&info->update, &tm); + /* TRANSLATOR: this is a format string for strftime + * see `man 3 strftime` for more details + */ + timeformat = g_locale_from_utf8 (_("%a, %b %d / %H:%M"), -1, + NULL, NULL, NULL); + if (!timeformat) { + strcpy (buf, "???"); + } + else if (strftime(buf, sizeof(buf), timeformat, &tm) <= 0) { + strcpy (buf, "???"); + } + g_free (timeformat); + + /* Convert to UTF-8 */ + utf8 = g_locale_to_utf8 (buf, -1, NULL, NULL, NULL); + strcpy (buf, utf8); + g_free (utf8); + } else { + strncpy(buf, _("Unknown observation time"), sizeof (buf)); + buf[sizeof(buf)-1] = '\0'; + } + + return buf; +} + +const gchar *weather_info_get_sky (WeatherInfo *info) +{ + g_return_val_if_fail(info != NULL, NULL); + if (!info->valid) + return "-"; + if (info->sky < 0) + return _("Unknown"); + return weather_sky_string(info->sky); +} + +const gchar *weather_info_get_conditions (WeatherInfo *info) +{ + g_return_val_if_fail(info != NULL, NULL); + if (!info->valid) + return "-"; + return weather_conditions_string(info->cond); +} + +static const gchar *temperature_string (gfloat far, TempUnit to_unit, gboolean round) +{ + static gchar buf[100]; + + switch (to_unit) { + case TEMP_UNIT_FAHRENHEIT: + if (!round) { + /* TRANSLATOR: This is the temperature in degrees Fahrenheit (\342\204\211 is the "DEGREE FAHRENHEIT" symbol) */ + g_snprintf(buf, sizeof (buf), _("%.1f \342\204\211"), far); + } else { + /* TRANSLATOR: This is the temperature in degrees Fahrenheit (\342\204\211 is the "DEGREE FAHRENHEIT" symbol) */ + g_snprintf(buf, sizeof (buf), _("%d \342\204\211"), (int)floor(far + 0.5)); + } + break; + case TEMP_UNIT_CENTIGRADE: + if (!round) { + /* TRANSLATOR: This is the temperature in degrees Celsius (\342\204\203 is the "DEGREE CELSIUS" symbol) */ + g_snprintf (buf, sizeof (buf), _("%.1f \342\204\203"), TEMP_F_TO_C(far)); + } else { + /* TRANSLATOR: This is the temperature in degrees Celsius (\342\204\203 is the "DEGREE CELSIUS" symbol) */ + g_snprintf (buf, sizeof (buf), _("%d \342\204\203"), (int)floor(TEMP_F_TO_C(far) + 0.5)); + } + break; + case TEMP_UNIT_KELVIN: + if (!round) { + /* TRANSLATOR: This is the temperature in kelvin */ + g_snprintf (buf, sizeof (buf), _("%.1f K"), TEMP_F_TO_K(far)); + } else { + /* TRANSLATOR: This is the temperature in kelvin */ + g_snprintf (buf, sizeof (buf), _("%d K"), (int)floor(TEMP_F_TO_K(far))); + } + break; + + case TEMP_UNIT_INVALID: + case TEMP_UNIT_DEFAULT: + default: + g_warning("Conversion to illegal temperature unit: %d", to_unit); + return (_("Unknown")); + } + + return buf; +} + +const gchar *weather_info_get_temp (WeatherInfo *info) +{ + g_return_val_if_fail(info != NULL, NULL); + + if (!info->valid) + return "-"; + if (info->temp < -500.0) + return _("Unknown"); + + return temperature_string (info->temp, info->temperature_unit, FALSE); +} + +const gchar *weather_info_get_dew (WeatherInfo *info) +{ + g_return_val_if_fail(info != NULL, NULL); + + if (!info->valid) + return "-"; + if (info->dew < -500.0) + return _("Unknown"); + + return temperature_string (info->dew, info->temperature_unit, FALSE); +} + +const gchar *weather_info_get_humidity (WeatherInfo *info) +{ + static gchar buf[20]; + gdouble humidity; + g_return_val_if_fail(info != NULL, NULL); + if (!info->valid) + return "-"; + + humidity = calc_humidity(info->temp, info->dew); + if (humidity < 0.0) + return _("Unknown"); + + /* TRANSLATOR: This is the humidity in percent */ + g_snprintf(buf, sizeof (buf), _("%.f%%"), humidity); + return buf; +} + +const gchar *weather_info_get_apparent (WeatherInfo *info) +{ + gdouble apparent; + g_return_val_if_fail(info != NULL, NULL); + if (!info->valid) + return "-"; + + apparent = calc_apparent(info); + if (apparent < -500.0) + return _("Unknown"); + + return temperature_string (apparent, info->temperature_unit, FALSE); +} + +static const gchar *windspeed_string (gfloat knots, SpeedUnit to_unit) +{ + static gchar buf[100]; + + switch (to_unit) { + case SPEED_UNIT_KNOTS: + /* TRANSLATOR: This is the wind speed in knots */ + g_snprintf(buf, sizeof (buf), _("%0.1f knots"), knots); + break; + case SPEED_UNIT_MPH: + /* TRANSLATOR: This is the wind speed in miles per hour */ + g_snprintf(buf, sizeof (buf), _("%.1f mph"), WINDSPEED_KNOTS_TO_MPH(knots)); + break; + case SPEED_UNIT_KPH: + /* TRANSLATOR: This is the wind speed in kilometers per hour */ + g_snprintf(buf, sizeof (buf), _("%.1f km/h"), WINDSPEED_KNOTS_TO_KPH(knots)); + break; + case SPEED_UNIT_MS: + /* TRANSLATOR: This is the wind speed in meters per second */ + g_snprintf(buf, sizeof (buf), _("%.1f m/s"), WINDSPEED_KNOTS_TO_MS(knots)); + break; + case SPEED_UNIT_BFT: + /* TRANSLATOR: This is the wind speed as a Beaufort force factor + * (commonly used in nautical wind estimation). + */ + g_snprintf (buf, sizeof (buf), _("Beaufort force %.1f"), + WINDSPEED_KNOTS_TO_BFT (knots)); + break; + case SPEED_UNIT_INVALID: + case SPEED_UNIT_DEFAULT: + default: + g_warning("Conversion to illegal speed unit: %d", to_unit); + return _("Unknown"); + } + + return buf; +} +const gchar *weather_info_get_wind (WeatherInfo *info) +{ + static gchar buf[200]; + g_return_val_if_fail(info != NULL, NULL); + if (!info->valid) + return "-"; + if (info->windspeed < 0.0 || info->wind < 0) + return _("Unknown"); + if (info->windspeed == 0.00) { + strncpy(buf, _("Calm"), sizeof(buf)); + buf[sizeof(buf)-1] = '\0'; + } else + /* TRANSLATOR: This is 'wind direction' / 'wind speed' */ + g_snprintf(buf, sizeof(buf), _("%s / %s"), + weather_wind_direction_string(info->wind), + windspeed_string(info->windspeed, info->speed_unit)); + return buf; +} + +const gchar *weather_info_get_pressure (WeatherInfo *info) +{ + static gchar buf[100]; + g_return_val_if_fail(info != NULL, NULL); + if (!info->valid) + return "-"; + if (info->pressure < 0.0) + return _("Unknown"); + + switch (info->pressure_unit) { + case PRESSURE_UNIT_INCH_HG: + /* TRANSLATOR: This is pressure in inches of mercury */ + g_snprintf (buf, sizeof (buf), _("%.2f inHg"), info->pressure); + break; + case PRESSURE_UNIT_MM_HG: + /* TRANSLATOR: This is pressure in millimeters of mercury */ + g_snprintf (buf, sizeof (buf), _("%.1f mmHg"), PRESSURE_INCH_TO_MM(info->pressure)); + break; + case PRESSURE_UNIT_KPA: + /* TRANSLATOR: This is pressure in kiloPascals */ + g_snprintf (buf, sizeof (buf), _("%.2f kPa"), PRESSURE_INCH_TO_KPA(info->pressure)); + break; + case PRESSURE_UNIT_HPA: + /* TRANSLATOR: This is pressure in hectoPascals */ + g_snprintf (buf, sizeof (buf), _("%.2f hPa"), PRESSURE_INCH_TO_HPA(info->pressure)); + break; + case PRESSURE_UNIT_MB: + /* TRANSLATOR: This is pressure in millibars */ + g_snprintf (buf, sizeof (buf), _("%.2f mb"), PRESSURE_INCH_TO_MB(info->pressure)); + break; + case PRESSURE_UNIT_ATM: + /* TRANSLATOR: This is pressure in atmospheres */ + g_snprintf (buf, sizeof (buf), _("%.3f atm"), PRESSURE_INCH_TO_ATM(info->pressure)); + break; + + case PRESSURE_UNIT_INVALID: + case PRESSURE_UNIT_DEFAULT: + default: + g_warning("Conversion to illegal pressure unit: %d", info->pressure_unit); + return _("Unknown"); + } + + return buf; +} + +const gchar *weather_info_get_visibility (WeatherInfo *info) +{ + static gchar buf[100]; + g_return_val_if_fail(info != NULL, NULL); + if (!info->valid) + return "-"; + if (info->visibility < 0.0) + return _("Unknown"); + + switch (info->distance_unit) { + case DISTANCE_UNIT_MILES: + /* TRANSLATOR: This is the visibility in miles */ + g_snprintf(buf, sizeof (buf), _("%.1f miles"), info->visibility); + break; + case DISTANCE_UNIT_KM: + /* TRANSLATOR: This is the visibility in kilometers */ + g_snprintf(buf, sizeof (buf), _("%.1f km"), VISIBILITY_SM_TO_KM(info->visibility)); + break; + case DISTANCE_UNIT_METERS: + /* TRANSLATOR: This is the visibility in meters */ + g_snprintf(buf, sizeof (buf), _("%.0fm"), VISIBILITY_SM_TO_M(info->visibility)); + break; + + case DISTANCE_UNIT_INVALID: + case DISTANCE_UNIT_DEFAULT: + default: + g_warning("Conversion to illegal visibility unit: %d", info->pressure_unit); + return _("Unknown"); + } + + return buf; +} + +const gchar *weather_info_get_sunrise (WeatherInfo *info) +{ + static gchar buf[200]; + struct tm tm; + + g_return_val_if_fail(info && info->location, NULL); + + if (!info->location->latlon_valid) + return "-"; + if (!info->valid) + return "-"; + if (!calc_sun (info)) + return "-"; + + localtime_r(&info->sunrise, &tm); + if (strftime(buf, sizeof(buf), _("%H:%M"), &tm) <= 0) + return "-"; + return buf; +} + +const gchar *weather_info_get_sunset (WeatherInfo *info) +{ + static gchar buf[200]; + struct tm tm; + + g_return_val_if_fail(info && info->location, NULL); + + if (!info->location->latlon_valid) + return "-"; + if (!info->valid) + return "-"; + if (!calc_sun (info)) + return "-"; + + localtime_r(&info->sunset, &tm); + if (strftime(buf, sizeof(buf), _("%H:%M"), &tm) <= 0) + return "-"; + return buf; +} + +const gchar *weather_info_get_forecast (WeatherInfo *info) +{ + g_return_val_if_fail(info != NULL, NULL); + return info->forecast; +} + +GdkPixbufAnimation *weather_info_get_radar (WeatherInfo *info) +{ + g_return_val_if_fail(info != NULL, NULL); + return info->radar; +} + +const gchar *weather_info_get_temp_summary (WeatherInfo *info) +{ + if (!info) + return NULL; + if (!info->valid || info->temp < -500.0) + return "--"; + + return temperature_string (info->temp, info->temperature_unit, TRUE); + +} + +gchar *weather_info_get_weather_summary (WeatherInfo *info) +{ + const gchar *buf; + g_return_val_if_fail(info != NULL, NULL); + if (!info->valid) + return g_strdup (_("Retrieval failed")); + buf = weather_info_get_conditions(info); + if (!strcmp(buf, "-")) + buf = weather_info_get_sky(info); + return g_strdup_printf ("%s: %s", weather_info_get_location_name (info), buf); +} + +const gchar * +weather_info_get_icon_name (WeatherInfo *info) +{ + WeatherConditions cond; + WeatherSky sky; + time_t current_time; + gboolean daytime; + + if (!info || !info->valid) + return NULL; + + cond = info->cond; + sky = info->sky; + + if (cond.significant) { + if (cond.phenomenon != PHENOMENON_NONE && + cond.qualifier == QUALIFIER_THUNDERSTORM) + return "weather-storm"; + + switch (cond.phenomenon) { + case PHENOMENON_NONE: + break; + + case PHENOMENON_DRIZZLE: + case PHENOMENON_RAIN: + case PHENOMENON_UNKNOWN_PRECIPITATION: + case PHENOMENON_HAIL: + case PHENOMENON_SMALL_HAIL: + return "weather-showers"; + + case PHENOMENON_SNOW: + case PHENOMENON_SNOW_GRAINS: + case PHENOMENON_ICE_PELLETS: + case PHENOMENON_ICE_CRYSTALS: + return "weather-snow"; + + case PHENOMENON_TORNADO: + case PHENOMENON_SQUALL: + return "weather-storm"; + + case PHENOMENON_MIST: + case PHENOMENON_FOG: + case PHENOMENON_SMOKE: + case PHENOMENON_VOLCANIC_ASH: + case PHENOMENON_SAND: + case PHENOMENON_HAZE: + case PHENOMENON_SPRAY: + case PHENOMENON_DUST: + case PHENOMENON_SANDSTORM: + case PHENOMENON_DUSTSTORM: + case PHENOMENON_FUNNEL_CLOUD: + case PHENOMENON_DUST_WHIRLS: + return "weather-fog"; + } + } + + current_time = time (NULL); + daytime = ((!info->sunValid) || + (current_time >= info->sunrise && + current_time < info->sunset)); + + switch (sky) { + case SKY_INVALID: + case SKY_CLEAR: + if (daytime) + return "weather-clear"; + else + return "weather-clear-night"; + + case SKY_BROKEN: + case SKY_SCATTERED: + case SKY_FEW: + if (daytime) + return "weather-few-clouds"; + else + return "weather-few-clouds-night"; + + case SKY_OVERCAST: + return "weather-overcast"; + } + + return NULL; +} diff --git a/libgweather/weather.h b/libgweather/weather.h new file mode 100644 index 0000000..2ecddd9 --- /dev/null +++ b/libgweather/weather.h @@ -0,0 +1,159 @@ +#ifndef __WEATHER_H_ +#define __WEATHER_H_ + +/* $Id: weather.h 10520 2007-11-14 21:49:57Z ryanl $ */ + +/* + * Papadimitriou Spiros <spapadim+@cs.cmu.edu> + * + * This code released under the GNU GPL. + * Read the file COPYING for more information. + * + * Public header for weather server functions. + * + */ + +#include <gdk-pixbuf/gdk-pixbuf-loader.h> + +G_BEGIN_DECLS + +/* + * Location + */ + +#define WEATHER_LOCATION_CODE_LEN 4 + +struct _WeatherLocation { + gchar *name; + gchar *code; + gchar *zone; + gchar *radar; + gboolean zone_valid; + gchar *coordinates; + gdouble latitude; + gdouble longitude; + gboolean latlon_valid; +}; + +typedef struct _WeatherLocation WeatherLocation; + +WeatherLocation * weather_location_new (const gchar *trans_name, + const gchar *code, + const gchar *zone, + const gchar *radar, + const gchar *coordinates); +WeatherLocation * weather_location_clone (const WeatherLocation *location); +void weather_location_free (WeatherLocation *location); +gboolean weather_location_equal (const WeatherLocation *location1, + const WeatherLocation *location2); + +/* + * Weather prefs + */ + +typedef enum _WeatherForecastType { + FORECAST_STATE, + FORECAST_ZONE +} WeatherForecastType; + +typedef enum { + TEMP_UNIT_INVALID = 0, + TEMP_UNIT_DEFAULT, + TEMP_UNIT_KELVIN, + TEMP_UNIT_CENTIGRADE, + TEMP_UNIT_FAHRENHEIT +} TempUnit; + +typedef enum { + SPEED_UNIT_INVALID = 0, + SPEED_UNIT_DEFAULT, + SPEED_UNIT_MS, /* metres per second */ + SPEED_UNIT_KPH, /* kilometres per hour */ + SPEED_UNIT_MPH, /* miles per hour */ + SPEED_UNIT_KNOTS, /* Knots */ + SPEED_UNIT_BFT /* Beaufort scale */ +} SpeedUnit; + +typedef enum { + PRESSURE_UNIT_INVALID = 0, + PRESSURE_UNIT_DEFAULT, + PRESSURE_UNIT_KPA, /* kiloPascal */ + PRESSURE_UNIT_HPA, /* hectoPascal */ + PRESSURE_UNIT_MB, /* 1 millibars = 1 hectoPascal */ + PRESSURE_UNIT_MM_HG, /* millimeters of mecury */ + PRESSURE_UNIT_INCH_HG, /* inches of mercury */ + PRESSURE_UNIT_ATM /* atmosphere */ +} PressureUnit; + +typedef enum { + DISTANCE_UNIT_INVALID = 0, + DISTANCE_UNIT_DEFAULT, + DISTANCE_UNIT_METERS, + DISTANCE_UNIT_KM, + DISTANCE_UNIT_MILES +} DistanceUnit; + +struct _WeatherPrefs { + WeatherForecastType type; + + gboolean radar; + const char *radar_custom_url; + + TempUnit temperature_unit; + SpeedUnit speed_unit; + PressureUnit pressure_unit; + DistanceUnit distance_unit; +}; + +typedef struct _WeatherPrefs WeatherPrefs; + +/* + * Weather Info + */ + +typedef struct _WeatherInfo WeatherInfo; + +typedef void (*WeatherInfoFunc) (WeatherInfo *info, gpointer data); + +WeatherInfo * _weather_info_fill (WeatherInfo *info, + WeatherLocation *location, + const WeatherPrefs *prefs, + WeatherInfoFunc cb, + gpointer data); +#define weather_info_new(location, prefs, cb, data) _weather_info_fill(NULL, (location), (prefs), (cb), (data)) +#define weather_info_update(info, prefs, cb, data) _weather_info_fill((info), NULL, (prefs), (cb), (data)) + +void weather_info_abort (WeatherInfo *info); +WeatherInfo * weather_info_clone (const WeatherInfo *info); +void weather_info_free (WeatherInfo *info); + +gboolean weather_info_is_valid (WeatherInfo *info); + +void weather_info_to_metric (WeatherInfo *info); +void weather_info_to_imperial (WeatherInfo *info); + +const WeatherLocation * weather_info_get_location (WeatherInfo *info); +const gchar * weather_info_get_location_name (WeatherInfo *info); +const gchar * weather_info_get_update (WeatherInfo *info); +const gchar * weather_info_get_sky (WeatherInfo *info); +const gchar * weather_info_get_conditions (WeatherInfo *info); +const gchar * weather_info_get_temp (WeatherInfo *info); +const gchar * weather_info_get_dew (WeatherInfo *info); +const gchar * weather_info_get_humidity (WeatherInfo *info); +const gchar * weather_info_get_wind (WeatherInfo *info); +const gchar * weather_info_get_pressure (WeatherInfo *info); +const gchar * weather_info_get_visibility (WeatherInfo *info); +const gchar * weather_info_get_apparent (WeatherInfo *info); +const gchar * weather_info_get_sunrise (WeatherInfo *info); +const gchar * weather_info_get_sunset (WeatherInfo *info); +const gchar * weather_info_get_forecast (WeatherInfo *info); +GdkPixbufAnimation * weather_info_get_radar (WeatherInfo *info); + +const gchar * weather_info_get_temp_summary (WeatherInfo *info); +gchar * weather_info_get_weather_summary(WeatherInfo *info); + +const gchar * weather_info_get_icon_name (WeatherInfo *info); +gint weather_info_next_sun_event (WeatherInfo *info); +G_END_DECLS + +#endif /* __WEATHER_H_ */ |