+#include "compat.h"
+
+static void aweather_bin_set_child(GtkBin *bin, GtkWidget *new)
+{
+ GtkWidget *old = gtk_bin_get_child(bin);
+ if (old)
+ gtk_widget_destroy(old);
+ gtk_container_add(GTK_CONTAINER(bin), new);
+ gtk_widget_show_all(new);
+}
+
+static gchar *_find_nearest(time_t time, GList *files,
+ gsize offset)
+{
+ g_debug("RadarSite: find_nearest ...");
+ time_t nearest_time = 0;
+ char *nearest_file = NULL;
+
+ struct tm tm = {};
+ for (GList *cur = files; cur; cur = cur->next) {
+ gchar *file = cur->data;
+ sscanf(file+offset, "%4d%2d%2d_%2d%2d",
+ &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
+ &tm.tm_hour, &tm.tm_min);
+ tm.tm_year -= 1900;
+ tm.tm_mon -= 1;
+ if (ABS(time - mktime(&tm)) <
+ ABS(time - nearest_time)) {
+ nearest_file = file;
+ nearest_time = mktime(&tm);
+ }
+ }
+
+ g_debug("RadarSite: find_nearest = %s", nearest_file);
+ if (nearest_file)
+ return g_strdup(nearest_file);
+ else
+ return NULL;
+}
+
+
+/**************
+ * RadarSites *
+ **************/
+typedef enum {
+ STATUS_UNLOADED,
+ STATUS_LOADING,
+ STATUS_LOADED,
+} RadarSiteStatus;
+struct _RadarSite {
+ /* Information */
+ city_t *city;
+ GritsMarker *marker; // Map marker for grits
+
+ /* Stuff from the parents */
+ GritsViewer *viewer;
+ GritsHttp *http;
+ GritsPrefs *prefs;
+ GtkWidget *pconfig;
+
+ /* When loaded */
+ gboolean hidden;
+ RadarSiteStatus status; // Loading status for the site
+ GtkWidget *config;
+ AWeatherLevel2 *level2; // The Level2 structure for the current volume
+
+ /* Internal data */
+ time_t time; // Current timestamp of the level2
+ gchar *message; // Error message set while updating
+ guint time_id; // "time-changed" callback ID
+ guint refresh_id; // "refresh" callback ID
+ guint location_id; // "locaiton-changed" callback ID
+ guint idle_source; // _site_update_end idle source
+};
+
+/* format: http://mesonet.agron.iastate.edu/data/nexrd2/raw/KABR/KABR_20090510_0323 */
+void _site_update_loading(gchar *file, goffset cur,
+ goffset total, gpointer _site)
+{
+ RadarSite *site = _site;
+ GtkWidget *progress_bar = gtk_bin_get_child(GTK_BIN(site->config));
+ double percent = (double)cur/total;
+ gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progress_bar), MIN(percent, 1.0));
+ gchar *msg = g_strdup_printf("Loading... %5.1f%% (%.2f/%.2f MB)",
+ percent*100, (double)cur/1000000, (double)total/1000000);
+ gtk_progress_bar_set_text(GTK_PROGRESS_BAR(progress_bar), msg);
+ g_free(msg);
+}
+gboolean _site_update_end(gpointer _site)
+{
+ RadarSite *site = _site;
+ if (site->message) {
+ g_warning("RadarSite: update_end - %s", site->message);
+ const char *fmt = "http://forecast.weather.gov/product.php?site=NWS&product=FTM&format=TXT&issuedby=%s";
+ char *uri = g_strdup_printf(fmt, site->city->code+1);
+ GtkWidget *box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
+ GtkWidget *msg = gtk_label_new(site->message);
+ GtkWidget *btn = gtk_link_button_new_with_label(uri, "View Radar Status");
+ gtk_box_set_homogeneous(GTK_BOX(box), TRUE);
+ gtk_box_pack_start(GTK_BOX(box), msg, TRUE, TRUE, 0);
+ gtk_box_pack_start(GTK_BOX(box), btn, TRUE, TRUE, 0);
+ aweather_bin_set_child(GTK_BIN(site->config), box);
+ g_free(uri);
+ } else {
+ aweather_bin_set_child(GTK_BIN(site->config),
+ aweather_level2_get_config(site->level2));
+ }
+ site->status = STATUS_LOADED;
+ site->idle_source = 0;
+ return FALSE;
+}
+gpointer _site_update_thread(gpointer _site)
+{
+ RadarSite *site = _site;
+ g_debug("RadarSite: update_thread - %s", site->city->code);
+ site->message = NULL;
+
+ gboolean offline = grits_viewer_get_offline(site->viewer);
+ gchar *nexrad_url = grits_prefs_get_string(site->prefs,
+ "aweather/nexrad_url", NULL);
+
+ /* Find nearest volume (temporally) */
+ g_debug("RadarSite: update_thread - find nearest - %s", site->city->code);
+ gchar *dir_list = g_strconcat(nexrad_url, "/", site->city->code,
+ "/", "dir.list", NULL);
+ GList *files = grits_http_available(site->http,
+ "^\\w{4}_\\d{8}_\\d{4}$", site->city->code,
+ "\\d+ (.*)", (offline ? NULL : dir_list));
+ g_free(dir_list);
+ gchar *nearest = _find_nearest(site->time, files, 5);
+ g_list_foreach(files, (GFunc)g_free, NULL);
+ g_list_free(files);
+ if (!nearest) {
+ site->message = "No suitable files found";
+ goto out;
+ }
+
+ /* Fetch new volume */
+ g_debug("RadarSite: update_thread - fetch");
+ gchar *local = g_strconcat(site->city->code, "/", nearest, NULL);
+ gchar *uri = g_strconcat(nexrad_url, "/", local, NULL);
+ gchar *file = grits_http_fetch(site->http, uri, local,
+ offline ? GRITS_LOCAL : GRITS_UPDATE,
+ _site_update_loading, site);
+ g_free(nexrad_url);
+ g_free(nearest);
+ g_free(local);
+ g_free(uri);
+ if (!file) {
+ site->message = "Fetch failed";
+ goto out;
+ }
+
+ /* Load and add new volume */
+ g_debug("RadarSite: update_thread - load - %s", site->city->code);
+ site->level2 = aweather_level2_new_from_file(
+ file, site->city->code, colormaps);
+ g_free(file);
+ if (!site->level2) {
+ site->message = "Load failed";
+ goto out;
+ }
+ grits_object_hide(GRITS_OBJECT(site->level2), site->hidden);
+ grits_viewer_add(site->viewer, GRITS_OBJECT(site->level2),
+ GRITS_LEVEL_WORLD+3, TRUE);
+
+out:
+ if (!site->idle_source)
+ site->idle_source = g_idle_add(_site_update_end, site);
+ return NULL;
+}
+void _site_update(RadarSite *site)