[gnome-calendar/wip/flb/weather-forecast: 103/135] weather: Change weather icon selection heuristic.



commit b1b5cf2d734f18a218ec0689bf8690226cf059ff
Author: Florian Brosch <flo brosch gmail com>
Date:   Thu Oct 12 23:51:57 2017 +0200

    weather: Change weather icon selection heuristic.
    
    The original version used conditions as key. This does
    not work out for all providers. They might return no
    value while gweather derives icons from other fields.

 src/gcal-weather-service.c | 243 +++++++++++++++++++++++++++++++--------------
 1 file changed, 168 insertions(+), 75 deletions(-)
---
diff --git a/src/gcal-weather-service.c b/src/gcal-weather-service.c
index b0fcfa7c..d76f97cc 100644
--- a/src/gcal-weather-service.c
+++ b/src/gcal-weather-service.c
@@ -20,6 +20,7 @@
 
 #include <geocode-glib/geocode-glib.h>
 #include <geoclue.h>
+#include <string.h>
 #include <math.h>
 
 #include "gcal-weather-service.h"
@@ -126,35 +127,39 @@ static void     on_gclue_client_stop                       (GClueClient
 static void     gcal_weather_service_set_max_days          (GcalWeatherService  *self,
                                                             guint                days);
 
-static void     gcal_weather_service_update_weather        (GWeatherInfo       *info,
-                                                            GcalWeatherService *self);
+static void     gcal_weather_service_update_weather        (GWeatherInfo        *info,
+                                                            GcalWeatherService  *self);
 
 /* Internal weather update timer API and callbacks */
-static void     gcal_weather_service_set_check_interval    (GcalWeatherService   *self,
-                                                            guint                 check_interval);
+static void     gcal_weather_service_set_check_interval    (GcalWeatherService  *self,
+                                                            guint                check_interval);
+
+
+static gssize   get_normalized_icon_name_len               (const gchar         *str);
 
-static void     gcal_weather_service_timer_stop            (GcalWeatherService *self);
+static gchar*   get_normalized_icon_name                   (GWeatherInfo        *gwi);
 
-static void     gcal_weather_service_timer_start           (GcalWeatherService *self);
+static gint     get_icon_name_sortkey                      (const gchar         *icon_name);
 
-static gboolean on_timer_timeout                           (GcalWeatherService *self);
+static void     gcal_weather_service_timer_stop            (GcalWeatherService  *self);
 
-static gboolean get_time_day_start                         (GcalWeatherService *self,
-                                                            GDate              *ret_date,
-                                                            gint64             *ret_unix);
+static void     gcal_weather_service_timer_start           (GcalWeatherService  *self);
 
-static inline gboolean get_gweather_temperature            (GWeatherInfo       *gwi,
-                                                            gdouble            *temp);
+static gboolean on_timer_timeout                           (GcalWeatherService  *self);
 
-static inline gboolean get_gweather_phenomenon             (GWeatherInfo                *gwi,
-                                                            GWeatherConditionPhenomenon *phenomenon);
+static gboolean get_time_day_start                         (GcalWeatherService  *self,
+                                                            GDate               *ret_date,
+                                                            gint64              *ret_unix);
 
-static gboolean compute_weather_info_data                  (GSList             *samples,
-                                                            gchar             **icon_name,
-                                                            gchar             **temperature);
+static inline gboolean get_gweather_temperature            (GWeatherInfo        *gwi,
+                                                            gdouble             *temp);
 
-static GSList*  preprocess_gweather_reports                (GcalWeatherService *self,
-                                                            GSList             *samples);
+static gboolean compute_weather_info_data                  (GSList              *samples,
+                                                            gchar              **icon_name,
+                                                            gchar              **temperature);
+
+static GSList*  preprocess_gweather_reports                (GcalWeatherService  *self,
+                                                            GSList              *samples);
 
 
 G_END_DECLS
@@ -335,6 +340,133 @@ gcal_weather_service_init (GcalWeatherService *self)
 
 
 
+/* drop_suffix:
+ * @str: A icon name to normalize.
+ *
+ * Translates a given weather icon name to the
+ * one to display for folded weather reports.
+ *
+ * Returns: Number of initial characters that
+ *          belong to the normalized name.
+ */
+static gssize
+get_normalized_icon_name_len (const gchar *str)
+{
+  const gchar suffix1[] = "-symbolic";
+  const gssize suffix1_len = G_N_ELEMENTS (suffix1) - 1;
+
+  const gchar suffix2[] = "-night";
+  const gssize suffix2_len = G_N_ELEMENTS (suffix2) - 1;
+
+  gssize clean_len;
+  gssize str_len;
+
+  g_return_val_if_fail (str != NULL, -1);
+
+  str_len = strlen (str);
+
+  clean_len = str_len - suffix1_len;
+  if (clean_len >= 0 && memcmp (suffix1, str + clean_len, suffix1_len) == 0)
+    str_len = clean_len;
+
+  clean_len = str_len - suffix2_len;
+  if (clean_len >= 0 && memcmp (suffix2, str + clean_len, suffix2_len) == 0)
+    str_len = clean_len;
+
+  return str_len;
+}
+
+
+
+/* get_normalized_icon_name:
+ * @str: A icon name to normalize.
+ *
+ * Translates a given weather icon name to the
+ * one to display for folded weather reports.
+ *
+ * Returns: (transfer full): A normalized icon name.
+ */
+static gchar*
+get_normalized_icon_name (GWeatherInfo* wi)
+{
+  const gchar pfx[] = "-symbolic";
+  const gsize pfx_size = G_N_ELEMENTS (pfx) - 1;
+
+  const gchar *str; /* unowned */
+  gssize       normalized_size;
+  gchar       *buffer = NULL; /* owned */
+  gsize        buffer_size;
+
+  g_return_val_if_fail (wi != NULL, NULL);
+  
+  str = gweather_info_get_icon_name (wi);
+  if (str == NULL)
+    return NULL;
+  
+  normalized_size = get_normalized_icon_name_len (str);
+  g_return_val_if_fail (normalized_size >= 0, NULL);
+  
+  buffer_size = normalized_size + pfx_size + 1;
+  buffer = g_malloc (buffer_size);
+
+  memcpy (buffer, str, normalized_size);
+  memcpy (buffer + normalized_size, pfx, pfx_size);
+  buffer[buffer_size - 1] = '\0';  
+
+  return buffer;
+}
+
+
+
+/* get_icon_name_sortkey:
+ *
+ * Returns a sort key for a given weather
+ * icon name and -1 for unknown ones.
+ *
+ * The lower the key, the better the weather.
+ */
+static gint
+get_icon_name_sortkey (const gchar *icon_name)
+{
+  /* Note that we can't use gweathers condition
+   * field as it is not necessarily holds valid values.
+   * libgweather uses its own heuristic to determine
+   * the icon to use. String matching is still better
+   * than copying their algorithm, I guess.
+   */
+
+  gssize normalized_name_len;
+
+  const gchar* icons[] =
+    { "weather-clear",
+      "weather-few-clouds",
+      "weather-overcast",
+      "weather-fog",
+      "weather-showers-scattered",
+      "weather-showers",
+      "weather-snow",
+      "weather-storm",
+      "weather-severe-alert"
+    };
+
+  g_return_val_if_fail (icon_name != NULL, -1);
+
+  normalized_name_len = get_normalized_icon_name_len (icon_name);
+  g_return_val_if_fail (normalized_name_len >= 0, -1);
+
+  for (int i = 0; i < G_N_ELEMENTS (icons); i++)
+    {
+      if (icons[i][normalized_name_len] == '\0' && strncmp (icon_name, icons[i], normalized_name_len) == 0)
+        return i;
+    }
+
+  g_warning ("Unknown weather icon '%s'", icon_name);
+
+  return -1;
+}
+
+
+
 /**************
  * < private >
  **************/
@@ -343,16 +475,13 @@ gcal_weather_service_init (GcalWeatherService *self)
 static gchar*
 gwc2str (GWeatherInfo *gwi)
 {
-    gchar *result = NULL; /* owned */
-
     g_autoptr (GDateTime) date = NULL;
     g_autofree gchar     *date_str = NULL;
     glong      update;
 
+    gchar     *icon_name; /* unowned */
     gdouble    temp;
 
-    GWeatherConditionPhenomenon phen;
-
     if (!gweather_info_get_value_update (gwi, &update))
         return g_strdup ("<null>");
 
@@ -360,14 +489,12 @@ gwc2str (GWeatherInfo *gwi)
     date_str = g_date_time_format (date, "%F %T"),
 
     get_gweather_temperature (gwi, &temp);
-    get_gweather_phenomenon (gwi, &phen);
-
+    icon_name = gweather_info_get_symbolic_icon_name (gwi);
 
-    result = g_strdup_printf ("(%s: t:%f, w:%d)",
-                              date_str,
-                              temp,
-                              phen);
-    return result;
+    return g_strdup_printf ("(%s: t:%f, w:%s)",
+                            date_str,
+                            temp,
+                            icon_name);
 }
 #endif
 
@@ -459,43 +586,6 @@ get_gweather_temperature (GWeatherInfo *gwi,
 
 
 
-/* get_gweather_phenomenon:
- * @gwi: #GWeatherInfo to extract temperatures from.
- * @phenomenon: (out): extracted phenomenon
- *
- * Extracts and sanitizes phenomenons.
- *
- * Returns: %TRUE for valid phenomenons.
- */
-static inline gboolean
-get_gweather_phenomenon (GWeatherInfo                *gwi,
-                         GWeatherConditionPhenomenon *phenomenon)
-{
-  GWeatherConditionQualifier _qualifier;
-  GWeatherConditionPhenomenon value;
-  gboolean valid;
-
-  *phenomenon = GWEATHER_PHENOMENON_INVALID;
-
-  g_return_val_if_fail (gwi != NULL, FALSE);
-  g_return_val_if_fail (phenomenon != NULL, FALSE);
-
-  valid = gweather_info_get_value_conditions (gwi, &value, &_qualifier);
-
-  if (valid && value != GWEATHER_PHENOMENON_INVALID && value >= 0 && value < GWEATHER_PHENOMENON_LAST)
-    {
-      *phenomenon = value;
-      return TRUE;
-    }
-  else
-    {
-      *phenomenon = GWEATHER_PHENOMENON_INVALID;
-      return FALSE;
-    }
-}
-
-
-
 /* compute_weather_info_data:
  * @samples: List of received #GWeatherInfos.
  * @icon_name: (out): (transfer full): weather icon name or %NULL.
@@ -510,7 +600,7 @@ compute_weather_info_data (GSList   *samples,
                            gchar   **icon_name,
                            gchar   **temperature)
 {
-  GWeatherConditionPhenomenon phenomenon_val = GWEATHER_PHENOMENON_INVALID;
+  gint          phenomenon_val = -1;
   GWeatherInfo *phenomenon_gwi = NULL; /* unowned */
   gdouble       temp_val = NAN;
   GWeatherInfo *temp_gwi = NULL; /* unowned */
@@ -531,18 +621,21 @@ compute_weather_info_data (GSList   *samples,
 
   for (iter = samples; iter != NULL; iter = iter->next)
     {
-      GWeatherInfo *gwi; /* unowned */
-      GWeatherConditionPhenomenon phenomenon;
-      gboolean valid_phenom;
-      gdouble temp;
+      GWeatherInfo  *gwi;       /* unowned */
+      const gchar   *icon_name; /* unowned */
+      gint     phenomenon = -1;
+      gdouble  temp;
       gboolean valid_temp;
 
       gwi = GWEATHER_INFO (iter->data);
 
-      valid_phenom = get_gweather_phenomenon (gwi, &phenomenon);
+      icon_name = gweather_info_get_icon_name (gwi);
+      if (icon_name != NULL)
+        phenomenon  = get_icon_name_sortkey (icon_name);
+
       valid_temp = get_gweather_temperature (gwi, &temp);
 
-      if (valid_phenom && (phenomenon_gwi == NULL || phenomenon > phenomenon_val))
+      if (icon_name != NULL && (phenomenon_gwi == NULL || phenomenon > phenomenon_val))
         {
           phenomenon_val = phenomenon;
           phenomenon_gwi = gwi;
@@ -557,7 +650,7 @@ compute_weather_info_data (GSList   *samples,
 
   if (phenomenon_gwi != NULL && temp_gwi != NULL)
     {
-      *icon_name = g_strdup (gweather_info_get_symbolic_icon_name (phenomenon_gwi));
+      *icon_name = get_normalized_icon_name (phenomenon_gwi);
       *temperature = gweather_info_get_temp (temp_gwi);
       return TRUE;
     }


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]