[Xfce4-commits] <xfce4-weather-plugin:master> Rewrite code for calculating forecast and current weather data.

Harald Judt noreply at xfce.org
Sun Dec 9 23:44:34 CET 2012


Updating branch refs/heads/master
         to 24d45e3256ac896a90e64bf3825ece68466bcbe2 (commit)
       from 29d0da2a09d1583e97b8a1fdf8d554079244571a (commit)

commit 24d45e3256ac896a90e64bf3825ece68466bcbe2
Author: Harald Judt <h.judt at gmx.at>
Date:   Sun Dec 9 19:25:09 2012 +0100

    Rewrite code for calculating forecast and current weather data.
    
    This should work much better now and is easier to read.

 panel-plugin/weather-data.c |  313 ++++++++++++++++++++++++++++---------------
 1 files changed, 207 insertions(+), 106 deletions(-)

diff --git a/panel-plugin/weather-data.c b/panel-plugin/weather-data.c
index bf4da51..7e50a2c 100644
--- a/panel-plugin/weather-data.c
+++ b/panel-plugin/weather-data.c
@@ -41,20 +41,28 @@
      ? g_strdup_printf(format, g_ascii_strtod(value, NULL)) \
      : g_strdup(""))
 
-#define INTERPOLATE_OR_COPY(var, radian)                                \
-    if (ipol)                                                           \
-        comb->location->var =                                           \
-            interpolate_gchar_value(start->location->var,               \
-                                    end->location->var,                 \
-                                    comb->start, comb->end,             \
-                                    comb->point, radian);               \
-    else                                                                \
+#define INTERPOLATE_OR_COPY(var, radian)                    \
+    if (ipol)                                               \
+        comb->location->var =                               \
+            interpolate_gchar_value(start->location->var,   \
+                                    end->location->var,     \
+                                    comb->start, comb->end, \
+                                    comb->point, radian);   \
+    else                                                    \
         comb->location->var = g_strdup(end->location->var);
 
 #define COMB_END_COPY(var)                              \
     comb->location->var = g_strdup(end->location->var);
 
 
+/* struct to store results from searches for point data */
+typedef struct {
+    GArray *before;
+    time_t point;
+    GArray *after;
+} point_data_results;
+
+
 /* convert string to a double value, returning backup value on error */
 gdouble
 string_to_double(const gchar *str,
@@ -292,47 +300,6 @@ is_night_time(const xml_astro *astro)
 
 
 /*
- * Calculate start and end of a daytime interval using given dates.
- * We ought to take one of the intervals supplied by the XML feed,
- * which gives the most consistent data and does not force too many
- * searches to find something that fits. The following chosen
- * intervals were pretty reliable for several tested locations at the
- * time of this writing and gave reasonable results:
- *   Morning:   08:00-14:00
- *   Afternoon: 14:00-20:00
- *   Evening:   20:00-02:00
- *   Night:     02:00-08:00
- */
-static void
-get_daytime_interval(struct tm *start_tm,
-                     struct tm *end_tm,
-                     const daytime dt)
-{
-    start_tm->tm_min = end_tm->tm_min = 0;
-    start_tm->tm_sec = end_tm->tm_sec = 0;
-    start_tm->tm_isdst = end_tm->tm_isdst = -1;
-    switch (dt) {
-    case MORNING:
-        start_tm->tm_hour = 8;
-        end_tm->tm_hour = 14;
-        break;
-    case AFTERNOON:
-        start_tm->tm_hour = 14;
-        end_tm->tm_hour = 20;
-        break;
-    case EVENING:
-        start_tm->tm_hour = 20;
-        end_tm->tm_hour = 26;
-        break;
-    case NIGHT:
-        start_tm->tm_hour = 26;
-        end_tm->tm_hour = 32;
-        break;
-    }
-}
-
-
-/*
  * Return wind direction name (S, N, W,...) for wind degrees.
  */
 static gchar*
@@ -740,44 +707,193 @@ xml_time_compare(gpointer a,
 }
 
 
+static void
+point_data_results_free(point_data_results *pdr)
+{
+    g_assert(pdr != NULL);
+    if (G_UNLIKELY(pdr == NULL))
+        return;
+
+    g_assert(pdr->before != NULL);
+    g_array_free(pdr->before, FALSE);
+    g_assert(pdr->after != NULL);
+    g_array_free(pdr->after, FALSE);
+    g_slice_free(point_data_results, pdr);
+    return;
+}
+
+/*
+ * Given an array of point data, find two points for which
+ * corresponding interval data can be found so that the interval is as
+ * small as possible, returning NULL if such interval data doesn't
+ * exist.
+ */
+static xml_time *
+find_smallest_interval(xml_weather *wd,
+                       const point_data_results *pdr)
+{
+    GArray *before = pdr->before, *after = pdr->after;
+    xml_time *ts_before, *ts_after, *found;
+    gint i, j;
+
+    for (i = before->len - 1; i >= 0; i--) {
+        ts_before = g_array_index(before, xml_time *, i);
+        for (j = 0; j < after->len; j++) {
+            ts_after = g_array_index(after, xml_time *, j);
+            found = get_timeslice(wd, ts_before->start, ts_after->end, NULL);
+            if (found)
+                return found;
+        }
+    }
+    return NULL;
+}
+
+
+/*
+ * Given an array of point data, find two points for which
+ * corresponding interval data can be found so that the interval is as
+ * big as possible, returning NULL if such interval data doesn't
+ * exist.
+ */
+static xml_time *
+find_largest_interval(xml_weather *wd,
+                      const point_data_results *pdr)
+{
+    GArray *before = pdr->before, *after = pdr->after;
+    xml_time *ts_before = NULL, *ts_after = NULL, *found = NULL;
+    gint i, j;
+
+    for (i = before->len - 1; i >= 0; i--) {
+        ts_before = g_array_index(before, xml_time *, i);
+        g_assert(ts_before != NULL);
+        for (j = after->len - 1; j >= 0; j--) {
+            ts_after = g_array_index(after, xml_time *, j);
+            found = get_timeslice(wd, ts_before->start, ts_after->end, NULL);
+            if (found) {
+                weather_debug("Found biggest interval:");
+                weather_dump(weather_dump_timeslice, found);
+                return found;
+            }
+        }
+    }
+    weather_debug("Could not find interval.");
+    return NULL;
+}
+
+
+/* find point data within certain limits around a point in time */
+static point_data_results *
+find_point_data(const xml_weather *wd,
+                const time_t point_t,
+                const gdouble min_diff,
+                const gdouble max_diff)
+{
+    point_data_results *found = NULL;
+    xml_time *timeslice;
+    guint i;
+    gdouble diff;
+
+    found = g_slice_new0(point_data_results);
+    found->before = g_array_new(FALSE, TRUE, sizeof(xml_time *));
+    found->after = g_array_new(FALSE, TRUE, sizeof(xml_time *));
+
+    weather_debug("Checking %d timeslices for point data.",
+                  wd->timeslices->len);
+    for (i = 0; i < wd->timeslices->len; i++) {
+        timeslice = g_array_index(wd->timeslices, xml_time *, i);
+        /* look only for point data, not intervals */
+        if (G_UNLIKELY(timeslice == NULL) || timeslice_is_interval(timeslice))
+            continue;
+
+        /* add point data if within limits */
+        diff = difftime(timeslice->end, point_t);
+        if (diff < 0) {   /* before point_t */
+            diff *= -1;
+            if (diff < min_diff || diff > max_diff)
+                continue;
+            g_array_append_val(found->before, timeslice);
+            weather_dump(weather_dump_timeslice, timeslice);
+        } else {          /* after point_t */
+            if (diff < min_diff || diff > max_diff)
+                continue;
+            g_array_append_val(found->after, timeslice);
+            weather_dump(weather_dump_timeslice, timeslice);
+        }
+    }
+    g_array_sort(found->before, (GCompareFunc) xml_time_compare);
+    g_array_sort(found->after, (GCompareFunc) xml_time_compare);
+    found->point = point_t;
+    weather_debug("Found %d timeslices with point data, "
+                  "%d before and %d after point_t.",
+                  (found->before->len + found->after->len),
+                  found->before->len, found->after->len);
+    return found;
+}
+
+
 xml_time *
 make_current_conditions(xml_weather *wd,
                         time_t now_t)
 {
-    xml_time *interval_data;
-    struct tm now_tm, start_tm, end_tm;
-    time_t end_t;
+    point_data_results *found = NULL;
+    xml_time *interval = NULL;
+    struct tm point_tm = *localtime(&now_t);
+    time_t point_t = now_t;
+    gint i = 0;
 
+    g_assert(wd != NULL);
     if (G_UNLIKELY(wd == NULL))
         return NULL;
 
-    /* search for the nearest and shortest interval data available,
-       using a maximum interval of 6 hours */
-    now_tm = *localtime(&now_t);
-    end_tm = start_tm = now_tm;
-
-    /* minimum interval is 1 hour */
-    end_t = time_calc_hour(end_tm, 1);
-    end_tm = *localtime(&end_t);
-
-    /* We want to keep the hour deviation as small as possible,
-       so let's try an interval with ±1 hour deviation first */
-    interval_data = find_shortest_timeslice(wd, start_tm, end_tm, -1, 1, 6);
-    if (interval_data == NULL) {
-        /* in case we were unsuccessful we might need to enlarge the
-           search radius */
-        interval_data = find_shortest_timeslice(wd, start_tm, end_tm,
-                                                -3, 3, 6);
-        if (interval_data == NULL)
-            /* and maybe it's necessary to try even harder... */
-            interval_data = find_shortest_timeslice(wd, start_tm, end_tm,
-                                                    -3, 6, 6);
+    /* there may not be a timeslice available for the current
+       interval, so look max three hours ahead */
+    while (i < 3 && interval == NULL) {
+        point_t = time_calc_hour(point_tm, i);
+        found = find_point_data(wd, point_t, 1, 4 * 3600);
+        interval = find_smallest_interval(wd, found);
+        point_data_results_free(found);
+        point_tm = *localtime(&point_t);
+        i++;
     }
-    if (interval_data == NULL)
+    weather_dump(weather_dump_timeslice, interval);
+    if (interval == NULL)
         return NULL;
 
-    /* create combined timeslice with interpolated point and interval data */
-    return make_combined_timeslice(wd, interval_data, &now_t);
+    return make_combined_timeslice(wd, interval, &now_t);
+}
+
+
+static time_t
+get_daytime(int day,
+            daytime dt)
+{
+    struct tm point_tm;
+    time_t point_t;
+
+    /* initialize times to the current day */
+    time(&point_t);
+    point_tm = *localtime(&point_t);
+
+    /* calculate daytime for the requested day */
+    point_tm.tm_mday += day;
+    point_tm.tm_min = point_tm.tm_sec = 0;
+    point_tm.tm_isdst = -1;
+    switch (dt) {
+    case MORNING:
+        point_tm.tm_hour = 9;
+        break;
+    case AFTERNOON:
+        point_tm.tm_hour = 15;
+        break;
+    case EVENING:
+        point_tm.tm_hour = 21;
+        break;
+    case NIGHT:
+        point_tm.tm_hour = 27;
+        break;
+    }
+    point_t = mktime(&point_tm);
+    return point_t;
 }
 
 
@@ -786,39 +902,24 @@ make_current_conditions(xml_weather *wd,
  */
 xml_time *
 make_forecast_data(xml_weather *wd,
-                   const int day,
-                   const daytime dt)
+                   int day,
+                   daytime dt)
 {
-    xml_time *interval_data = NULL;
-    struct tm start_tm, end_tm;
-    time_t now_t, start_t, end_t;
-    gint interval;
-
-    /* initialize times to the current day */
-    time(&now_t);
-    start_tm = *localtime(&now_t);
-    end_tm = *localtime(&now_t);
+    point_data_results *found = NULL;
+    xml_time *interval = NULL;
+    time_t point_t;
 
-    /* calculate daytime interval start and end times for the requested day */
-    start_tm.tm_mday += day;
-    end_tm.tm_mday += day;
-    get_daytime_interval(&start_tm, &end_tm, dt);
-    start_t = mktime(&start_tm);
-    end_t = mktime(&end_tm);
+    g_assert(wd != NULL);
+    if (G_UNLIKELY(wd == NULL))
+        return NULL;
 
-    /* next find biggest possible (limited by daytime) interval data
-       using a maximum deviation of ±3 hours */
-    while ((interval = (gint) (difftime(end_t, start_t) / 3600)) > 0) {
-        interval_data = find_timeslice(wd, start_tm, end_tm, -3, 3);
-        if (interval_data != NULL)
-            break;
-        end_t = time_calc_hour(end_tm, -1);
-        end_tm = *localtime(&end_t);
-    }
-    if (interval_data == NULL)
+    point_t = get_daytime(day, dt);
+    found = find_point_data(wd, point_t, 1, 5 * 3600);
+    interval = find_largest_interval(wd, found);
+    point_data_results_free(found);
+    weather_dump(weather_dump_timeslice, interval);
+    if (interval == NULL)
         return NULL;
 
-    /* create combined timeslice with non-interpolated point
-       and interval data */
-    return make_combined_timeslice(wd, interval_data, NULL);
+    return make_combined_timeslice(wd, interval, &point_t);
 }


More information about the Xfce4-commits mailing list