1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2 /* weather.c - Overall weather server functions
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, see
16 * <http://www.gnu.org/licenses/>.
17 */
18
19 #ifdef HAVE_CONFIG_H
20 #include <config.h>
21 #endif
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <assert.h>
26 #include <string.h>
27 #include <ctype.h>
28 #include <math.h>
29 #include <fenv.h>
30
31 #ifdef HAVE_VALUES_H
32 #include <values.h>
33 #endif
34
35 #include <time.h>
36 #include <unistd.h>
37
38 #include <gdk-pixbuf/gdk-pixbuf.h>
39
40 #define MATEWEATHER_I_KNOW_THIS_IS_UNSTABLE
41 #include "weather.h"
42 #include "weather-priv.h"
43
44 #define MOON_PHASES 36
45
46 /**
47 * SECTION:weather
48 * @Title: weather
49 */
50
51 static void _weather_internal_check (void);
52
53
54 static inline void
mateweather_gettext_init(void)55 mateweather_gettext_init (void)
56 {
57 static gsize mateweather_gettext_initialized = FALSE;
58
59 if (G_UNLIKELY (g_once_init_enter (&mateweather_gettext_initialized))) {
60 bindtextdomain (GETTEXT_PACKAGE, MATELOCALEDIR);
61 #ifdef HAVE_BIND_TEXTDOMAIN_CODESET
62 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
63 #endif
64 g_once_init_leave (&mateweather_gettext_initialized, TRUE);
65 }
66 }
67
68 const char *
mateweather_gettext(const char * str)69 mateweather_gettext (const char *str)
70 {
71 mateweather_gettext_init ();
72 return dgettext (GETTEXT_PACKAGE, str);
73 }
74
75 const char *
mateweather_dpgettext(const char * context,const char * str)76 mateweather_dpgettext (const char *context,
77 const char *str)
78 {
79 mateweather_gettext_init ();
80 return g_dpgettext2 (GETTEXT_PACKAGE, context, str);
81 }
82
83 /*
84 * Convert string of the form "DD-MM-SSH" to radians
85 * DD:degrees (to 3 digits), MM:minutes, SS:seconds H:hemisphere (NESW)
86 * Return value is positive for N,E; negative for S,W.
87 */
88 static gdouble
dmsh2rad(const gchar * latlon)89 dmsh2rad (const gchar *latlon)
90 {
91 char *p1, *p2;
92 int deg, min, sec, dir;
93 gdouble value;
94
95 if (latlon == NULL)
96 return DBL_MAX;
97 p1 = strchr (latlon, '-');
98 p2 = strrchr (latlon, '-');
99 if (p1 == NULL || p1 == latlon) {
100 return DBL_MAX;
101 } else if (p1 == p2) {
102 sscanf (latlon, "%d-%d", °, &min);
103 sec = 0;
104 } else if (p2 == 1 + p1) {
105 return DBL_MAX;
106 } else {
107 sscanf (latlon, "%d-%d-%d", °, &min, &sec);
108 }
109 if (deg > 180 || min >= 60 || sec >= 60)
110 return DBL_MAX;
111 value = (gdouble)((deg * 60 + min) * 60 + sec) * M_PI / 648000.;
112
113 dir = g_ascii_toupper (latlon[strlen (latlon) - 1]);
114 if (dir == 'W' || dir == 'S')
115 value = -value;
116 else if (dir != 'E' && dir != 'N' && (value != 0.0 || dir != '0'))
117 value = DBL_MAX;
118 return value;
119 }
120
121 WeatherLocation *
weather_location_new(const gchar * name,const gchar * code,const gchar * zone,const gchar * radar,const gchar * coordinates,const gchar * country_code,const gchar * tz_hint)122 weather_location_new (const gchar *name, const gchar *code,
123 const gchar *zone, const gchar *radar,
124 const gchar *coordinates,
125 const gchar *country_code,
126 const gchar *tz_hint)
127 {
128 WeatherLocation *location;
129
130 _weather_internal_check ();
131
132 location = g_new (WeatherLocation, 1);
133
134 /* name and metar code must be set */
135 location->name = g_strdup (name);
136 location->code = g_strdup (code);
137
138 if (zone) {
139 location->zone = g_strdup (zone);
140 } else {
141 location->zone = g_strdup ("------");
142 }
143
144 if (radar) {
145 location->radar = g_strdup (radar);
146 } else {
147 location->radar = g_strdup ("---");
148 }
149
150 if (location->zone[0] == '-') {
151 location->zone_valid = FALSE;
152 } else {
153 location->zone_valid = TRUE;
154 }
155
156 location->coordinates = NULL;
157 if (coordinates)
158 {
159 char **pieces;
160
161 pieces = g_strsplit (coordinates, " ", -1);
162
163 if (g_strv_length (pieces) == 2)
164 {
165 location->coordinates = g_strdup (coordinates);
166 location->latitude = dmsh2rad (pieces[0]);
167 location->longitude = dmsh2rad (pieces[1]);
168 }
169
170 g_strfreev (pieces);
171 }
172
173 if (!location->coordinates)
174 {
175 location->coordinates = g_strdup ("---");
176 location->latitude = DBL_MAX;
177 location->longitude = DBL_MAX;
178 }
179
180 location->latlon_valid = (location->latitude < DBL_MAX && location->longitude < DBL_MAX);
181
182 location->country_code = g_strdup (country_code);
183 location->tz_hint = g_strdup (tz_hint);
184
185 return location;
186 }
187
188 WeatherLocation *
weather_location_clone(const WeatherLocation * location)189 weather_location_clone (const WeatherLocation *location)
190 {
191 WeatherLocation *clone;
192
193 g_return_val_if_fail (location != NULL, NULL);
194
195 clone = weather_location_new (location->name,
196 location->code, location->zone,
197 location->radar, location->coordinates,
198 location->country_code, location->tz_hint);
199 clone->latitude = location->latitude;
200 clone->longitude = location->longitude;
201 clone->latlon_valid = location->latlon_valid;
202 return clone;
203 }
204
205 void
weather_location_free(WeatherLocation * location)206 weather_location_free (WeatherLocation *location)
207 {
208 if (location) {
209 g_free (location->name);
210 g_free (location->code);
211 g_free (location->zone);
212 g_free (location->radar);
213 g_free (location->coordinates);
214 g_free (location->country_code);
215 g_free (location->tz_hint);
216
217 g_free (location);
218 }
219 }
220
221 gboolean
weather_location_equal(const WeatherLocation * location1,const WeatherLocation * location2)222 weather_location_equal (const WeatherLocation *location1, const WeatherLocation *location2)
223 {
224 /* if something is NULL, then it's TRUE if and only if both are NULL) */
225 if (location1 == NULL || location2 == NULL)
226 return (location1 == location2);
227 if (!location1->code || !location2->code)
228 return (location1->code == location2->code);
229 if (!location1->name || !location2->name)
230 return (location1->name == location2->name);
231
232 return ((strcmp (location1->code, location2->code) == 0) &&
233 (strcmp (location1->name, location2->name) == 0));
234 }
235
236 static const gchar *wind_direction_str[] = {
237 N_("Variable"),
238 N_("North"), N_("North - NorthEast"), N_("Northeast"), N_("East - NorthEast"),
239 N_("East"), N_("East - Southeast"), N_("Southeast"), N_("South - Southeast"),
240 N_("South"), N_("South - Southwest"), N_("Southwest"), N_("West - Southwest"),
241 N_("West"), N_("West - Northwest"), N_("Northwest"), N_("North - Northwest")
242 };
243
244 const gchar *
weather_wind_direction_string(WeatherWindDirection wind)245 weather_wind_direction_string (WeatherWindDirection wind)
246 {
247 if (wind <= WIND_INVALID || wind >= WIND_LAST)
248 return _("Invalid");
249
250 return _(wind_direction_str[(int)wind]);
251 }
252
253 static const gchar *sky_str[] = {
254 N_("Clear Sky"),
255 N_("Broken clouds"),
256 N_("Scattered clouds"),
257 N_("Few clouds"),
258 N_("Overcast")
259 };
260
261 const gchar *
weather_sky_string(WeatherSky sky)262 weather_sky_string (WeatherSky sky)
263 {
264 if (sky <= SKY_INVALID || sky >= SKY_LAST)
265 return _("Invalid");
266
267 return _(sky_str[(int)sky]);
268 }
269
270
271 /*
272 * Even though tedious, I switched to a 2D array for weather condition
273 * strings, in order to facilitate internationalization, esp. for languages
274 * with genders.
275 */
276
277 /*
278 * Almost all reportable combinations listed in
279 * http://www.crh.noaa.gov/arx/wx.tbl.php are entered below, except those
280 * having 2 qualifiers mixed together [such as "Blowing snow in vicinity"
281 * (VCBLSN), "Thunderstorm in vicinity" (VCTS), etc].
282 * Combinations that are not possible are filled in with "??".
283 * Some other exceptions not handled yet, such as "SN BLSN" which has
284 * special meaning.
285 */
286
287 /*
288 * Note, magic numbers, when you change the size here, make sure to change
289 * the below function so that new values are recognized
290 */
291 /* NONE VICINITY LIGHT MODERATE HEAVY SHALLOW PATCHES PARTIAL THUNDERSTORM BLOWING SHOWERS DRIFTING FREEZING */
292 /* *******************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************/
293 static const gchar *conditions_str[24][13] = {
294 /* Translators: If you want to know what "blowing" "shallow" "partial"
295 * etc means, you can go to http://www.weather.com/glossary/ and
296 * http://www.crh.noaa.gov/arx/wx.tbl.php */
297 /* NONE */ {"??", "??", "??", "??", "??", "??", "??", "??", N_("Thunderstorm"), "??", "??", "??", "??" },
298 /* DRIZZLE */ {N_("Drizzle"), "??", N_("Light drizzle"), N_("Moderate drizzle"), N_("Heavy drizzle"), "??", "??", "??", "??", "??", "??", "??", N_("Freezing drizzle") },
299 /* RAIN */ {N_("Rain"), "??", N_("Light rain"), N_("Moderate rain"), N_("Heavy rain"), "??", "??", "??", N_("Thunderstorm"), "??", N_("Rain showers"), "??", N_("Freezing rain") },
300 /* SNOW */ {N_("Snow"), "??", N_("Light snow"), N_("Moderate snow"), N_("Heavy snow"), "??", "??", "??", N_("Snowstorm"), N_("Blowing snowfall"), N_("Snow showers"), N_("Drifting snow"), "??" },
301 /* SNOW_GRAINS */ {N_("Snow grains"), "??", N_("Light snow grains"), N_("Moderate snow grains"), N_("Heavy snow grains"), "??", "??", "??", "??", "??", "??", "??", "??" },
302 /* ICE_CRYSTALS */ {N_("Ice crystals"), "??", "??", N_("Ice crystals"), "??", "??", "??", "??", "??", "??", "??", "??", "??" },
303 /* 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"), "??", "??" },
304 /* HAIL */ {N_("Hail"), "??", "??", N_("Hail"), "??", "??", "??", "??", N_("Hailstorm"), "??", N_("Hail showers"), "??", "??", },
305 /* SMALL_HAIL */ {N_("Small hail"), "??", "??", N_("Small hail"), "??", "??", "??", "??", N_("Small hailstorm"), "??", N_("Showers of small hail"), "??", "??" },
306 /* PRECIPITATION */ {N_("Unknown precipitation"), "??", "??", "??", "??", "??", "??", "??", "??", "??", "??", "??", "??" },
307 /* MIST */ {N_("Mist"), "??", "??", N_("Mist"), "??", "??", "??", "??", "??", "??", "??", "??", "??" },
308 /* FOG */ {N_("Fog"), N_("Fog in the vicinity") , "??", N_("Fog"), "??", N_("Shallow fog"), N_("Patches of fog"), N_("Partial fog"), "??", "??", "??", "??", N_("Freezing fog") },
309 /* SMOKE */ {N_("Smoke"), "??", "??", N_("Smoke"), "??", "??", "??", "??", "??", "??", "??", "??", "??" },
310 /* VOLCANIC_ASH */ {N_("Volcanic ash"), "??", "??", N_("Volcanic ash"), "??", "??", "??", "??", "??", "??", "??", "??", "??" },
311 /* SAND */ {N_("Sand"), "??", "??", N_("Sand"), "??", "??", "??", "??", "??", N_("Blowing sand"), "", N_("Drifting sand"), "??" },
312 /* HAZE */ {N_("Haze"), "??", "??", N_("Haze"), "??", "??", "??", "??", "??", "??", "??", "??", "??" },
313 /* SPRAY */ {"??", "??", "??", "??", "??", "??", "??", "??", "??", N_("Blowing sprays"), "??", "??", "??" },
314 /* DUST */ {N_("Dust"), "??", "??", N_("Dust"), "??", "??", "??", "??", "??", N_("Blowing dust"), "??", N_("Drifting dust"), "??" },
315 /* SQUALL */ {N_("Squall"), "??", "??", N_("Squall"), "??", "??", "??", "??", "??", "??", "??", "??", "??" },
316 /* SANDSTORM */ {N_("Sandstorm"), N_("Sandstorm in the vicinity") , "??", N_("Sandstorm"), N_("Heavy sandstorm"), "??", "??", "??", "??", "??", "??", "??", "??" },
317 /* DUSTSTORM */ {N_("Duststorm"), N_("Duststorm in the vicinity") , "??", N_("Duststorm"), N_("Heavy duststorm"), "??", "??", "??", "??", "??", "??", "??", "??" },
318 /* FUNNEL_CLOUD */ {N_("Funnel cloud"), "??", "??", "??", "??", "??", "??", "??", "??", "??", "??", "??", "??" },
319 /* TORNADO */ {N_("Tornado"), "??", "??", "??", "??", "??", "??", "??", "??", "??", "??", "??", "??" },
320 /* DUST_WHIRLS */ {N_("Dust whirls"), N_("Dust whirls in the vicinity") , "??", N_("Dust whirls"), "??", "??", "??", "??", "??", "??", "??", "??", "??" }
321 };
322
323 const gchar *
weather_conditions_string(WeatherConditions cond)324 weather_conditions_string (WeatherConditions cond)
325 {
326 const gchar *str;
327
328 if (!cond.significant) {
329 return "-";
330 } else {
331 if (cond.phenomenon > PHENOMENON_INVALID &&
332 cond.phenomenon < PHENOMENON_LAST &&
333 cond.qualifier > QUALIFIER_INVALID &&
334 cond.qualifier < QUALIFIER_LAST)
335 str = _(conditions_str[(int)cond.phenomenon][(int)cond.qualifier]);
336 else
337 str = _("Invalid");
338 return (strlen (str) > 0) ? str : "-";
339 }
340 }
341
342 /* Locals turned global to facilitate asynchronous HTTP requests */
343
344
345 gboolean
requests_init(WeatherInfo * info)346 requests_init (WeatherInfo *info)
347 {
348 if (info->requests_pending)
349 return FALSE;
350
351 return TRUE;
352 }
353
request_done(WeatherInfo * info,gboolean ok)354 void request_done (WeatherInfo *info, gboolean ok)
355 {
356 if (ok) {
357 (void) calc_sun (info);
358 info->moonValid = info->valid && calc_moon (info);
359 }
360 if (!--info->requests_pending)
361 info->finish_cb (info, info->cb_data);
362 }
363
364 /* it's OK to pass in NULL */
365 void
free_forecast_list(WeatherInfo * info)366 free_forecast_list (WeatherInfo *info)
367 {
368 GSList *p;
369
370 if (!info)
371 return;
372
373 for (p = info->forecast_list; p; p = p->next)
374 weather_info_free (p->data);
375
376 if (info->forecast_list) {
377 g_slist_free (info->forecast_list);
378 info->forecast_list = NULL;
379 }
380 }
381
382 /* Relative humidity computation - thanks to <Olof.Oberg@modopaper.modogroup.com> */
383
384 static inline gdouble
calc_humidity(gdouble temp,gdouble dewp)385 calc_humidity (gdouble temp, gdouble dewp)
386 {
387 gdouble esat, esurf;
388
389 if (temp > -500.0 && dewp > -500.0) {
390 temp = TEMP_F_TO_C (temp);
391 dewp = TEMP_F_TO_C (dewp);
392
393 esat = 6.11 * pow (10.0, (7.5 * temp) / (237.7 + temp));
394 esurf = 6.11 * pow (10.0, (7.5 * dewp) / (237.7 + dewp));
395 } else {
396 esurf = -1.0;
397 esat = 1.0;
398 }
399 return ((esurf/esat) * 100.0);
400 }
401
402 static inline gdouble
calc_apparent(WeatherInfo * info)403 calc_apparent (WeatherInfo *info)
404 {
405 gdouble temp = info->temp;
406 gdouble wind = WINDSPEED_KNOTS_TO_MPH (info->windspeed);
407 gdouble apparent = -1000.;
408
409 /*
410 * Wind chill calculations as of 01-Nov-2001
411 * http://www.nws.noaa.gov/om/windchill/index.shtml
412 * Some pages suggest that the formula will soon be adjusted
413 * to account for solar radiation (bright sun vs cloudy sky)
414 */
415 if (temp <= 50.0) {
416 if (wind > 3.0) {
417 gdouble v = pow (wind, 0.16);
418 apparent = 35.74 + 0.6215 * temp - 35.75 * v + 0.4275 * temp * v;
419 } else if (wind >= 0.) {
420 apparent = temp;
421 }
422 }
423 /*
424 * Heat index calculations:
425 * http://www.srh.noaa.gov/fwd/heatindex/heat5.html
426 */
427 else if (temp >= 80.0) {
428 if (info->temp >= -500. && info->dew >= -500.) {
429 gdouble humidity = calc_humidity (info->temp, info->dew);
430 gdouble t2 = temp * temp;
431 gdouble h2 = humidity * humidity;
432
433 #if 1
434 /*
435 * A really precise formula. Note that overall precision is
436 * constrained by the accuracy of the instruments and that the
437 * we receive the temperature and dewpoints as integers.
438 */
439 gdouble t3 = t2 * temp;
440 gdouble h3 = h2 * temp;
441
442 apparent = 16.923
443 + 0.185212 * temp
444 + 5.37941 * humidity
445 - 0.100254 * temp * humidity
446 + 9.41695e-3 * t2
447 + 7.28898e-3 * h2
448 + 3.45372e-4 * t2 * humidity
449 - 8.14971e-4 * temp * h2
450 + 1.02102e-5 * t2 * h2
451 - 3.8646e-5 * t3
452 + 2.91583e-5 * h3
453 + 1.42721e-6 * t3 * humidity
454 + 1.97483e-7 * temp * h3
455 - 2.18429e-8 * t3 * h2
456 + 8.43296e-10 * t2 * h3
457 - 4.81975e-11 * t3 * h3;
458 #else
459 /*
460 * An often cited alternative: values are within 5 degrees for
461 * most ranges between 10% and 70% humidity and to 110 degrees.
462 */
463 apparent = - 42.379
464 + 2.04901523 * temp
465 + 10.14333127 * humidity
466 - 0.22475541 * temp * humidity
467 - 6.83783e-3 * t2
468 - 5.481717e-2 * h2
469 + 1.22874e-3 * t2 * humidity
470 + 8.5282e-4 * temp * h2
471 - 1.99e-6 * t2 * h2;
472 #endif
473 }
474 } else {
475 apparent = temp;
476 }
477
478 return apparent;
479 }
480
481 WeatherInfo *
_weather_info_fill(WeatherInfo * info,WeatherLocation * location,const WeatherPrefs * prefs,WeatherInfoFunc cb,gpointer data)482 _weather_info_fill (WeatherInfo *info,
483 WeatherLocation *location,
484 const WeatherPrefs *prefs,
485 WeatherInfoFunc cb,
486 gpointer data)
487 {
488 g_return_val_if_fail (((info == NULL) && (location != NULL)) || \
489 ((info != NULL) && (location == NULL)), NULL);
490 g_return_val_if_fail (prefs != NULL, NULL);
491
492 /* FIXME: i'm not sure this works as intended anymore */
493 if (!info) {
494 info = g_new0 (WeatherInfo, 1);
495 info->requests_pending = 0;
496 info->location = weather_location_clone (location);
497 } else {
498 location = info->location;
499 if (info->forecast)
500 g_free (info->forecast);
501 info->forecast = NULL;
502
503 free_forecast_list (info);
504
505 if (info->radar != NULL) {
506 g_object_unref (info->radar);
507 info->radar = NULL;
508 }
509 }
510
511 /* Update in progress */
512 if (!requests_init (info)) {
513 return NULL;
514 }
515
516 /* Defaults (just in case...) */
517 /* Well, no just in case anymore. We may actually fail to fetch some
518 * fields. */
519 info->forecast_type = prefs->type;
520
521 info->temperature_unit = prefs->temperature_unit;
522 info->speed_unit = prefs->speed_unit;
523 info->pressure_unit = prefs->pressure_unit;
524 info->distance_unit = prefs->distance_unit;
525
526 info->update = 0;
527 info->sky = -1;
528 info->cond.significant = FALSE;
529 info->cond.phenomenon = PHENOMENON_NONE;
530 info->cond.qualifier = QUALIFIER_NONE;
531 info->temp = -1000.0;
532 info->tempMinMaxValid = FALSE;
533 info->temp_min = -1000.0;
534 info->temp_max = -1000.0;
535 info->dew = -1000.0;
536 info->wind = -1;
537 info->windspeed = -1;
538 info->pressure = -1.0;
539 info->visibility = -1.0;
540 info->sunriseValid = FALSE;
541 info->sunsetValid = FALSE;
542 info->moonValid = FALSE;
543 info->sunrise = 0;
544 info->sunset = 0;
545 info->moonphase = 0;
546 info->moonlatitude = 0;
547 info->forecast = NULL;
548 info->forecast_list = NULL;
549 info->radar = NULL;
550 info->radar_url = prefs->radar && prefs->radar_custom_url ?
551 g_strdup (prefs->radar_custom_url) : NULL;
552 info->finish_cb = cb;
553 info->cb_data = data;
554
555 if (!info->session) {
556 info->session = soup_session_new ();
557 }
558
559 metar_start_open (info);
560 iwin_start_open (info);
561
562 if (prefs->radar) {
563 wx_start_open (info);
564 }
565
566 return info;
567 }
568
569 void
weather_info_abort(WeatherInfo * info)570 weather_info_abort (WeatherInfo *info)
571 {
572 g_return_if_fail (info != NULL);
573
574 if (info->session) {
575 soup_session_abort (info->session);
576 info->requests_pending = 0;
577 }
578 }
579
580 WeatherInfo *
weather_info_clone(const WeatherInfo * info)581 weather_info_clone (const WeatherInfo *info)
582 {
583 WeatherInfo *clone;
584
585 g_return_val_if_fail (info != NULL, NULL);
586
587 clone = g_new (WeatherInfo, 1);
588
589
590 /* move everything */
591 memmove (clone, info, sizeof (WeatherInfo));
592
593
594 /* special moves */
595 clone->location = weather_location_clone (info->location);
596 /* This handles null correctly */
597 clone->forecast = g_strdup (info->forecast);
598 clone->radar_url = g_strdup (info->radar_url);
599
600 if (info->forecast_list) {
601 GSList *p;
602
603 clone->forecast_list = NULL;
604 for (p = info->forecast_list; p; p = p->next) {
605 clone->forecast_list = g_slist_prepend (clone->forecast_list, weather_info_clone (p->data));
606 }
607
608 clone->forecast_list = g_slist_reverse (clone->forecast_list);
609 }
610
611 clone->radar = info->radar;
612 if (clone->radar != NULL)
613 g_object_ref (clone->radar);
614
615 return clone;
616 }
617
618 void
weather_info_free(WeatherInfo * info)619 weather_info_free (WeatherInfo *info)
620 {
621 if (!info)
622 return;
623
624 weather_info_abort (info);
625 if (info->session)
626 g_object_unref (info->session);
627
628 weather_location_free (info->location);
629 info->location = NULL;
630
631 g_free (info->forecast);
632 info->forecast = NULL;
633
634 free_forecast_list (info);
635
636 if (info->radar != NULL) {
637 g_object_unref (info->radar);
638 info->radar = NULL;
639 }
640
641 g_free (info);
642 }
643
644 gboolean
weather_info_is_valid(WeatherInfo * info)645 weather_info_is_valid (WeatherInfo *info)
646 {
647 g_return_val_if_fail (info != NULL, FALSE);
648 return info->valid;
649 }
650
651 gboolean
weather_info_network_error(WeatherInfo * info)652 weather_info_network_error (WeatherInfo *info)
653 {
654 g_return_val_if_fail (info != NULL, FALSE);
655 return info->network_error;
656 }
657
658 void
weather_info_to_metric(WeatherInfo * info)659 weather_info_to_metric (WeatherInfo *info)
660 {
661 g_return_if_fail (info != NULL);
662
663 info->temperature_unit = TEMP_UNIT_CENTIGRADE;
664 info->speed_unit = SPEED_UNIT_MS;
665 info->pressure_unit = PRESSURE_UNIT_HPA;
666 info->distance_unit = DISTANCE_UNIT_METERS;
667 }
668
669 void
weather_info_to_imperial(WeatherInfo * info)670 weather_info_to_imperial (WeatherInfo *info)
671 {
672 g_return_if_fail (info != NULL);
673
674 info->temperature_unit = TEMP_UNIT_FAHRENHEIT;
675 info->speed_unit = SPEED_UNIT_MPH;
676 info->pressure_unit = PRESSURE_UNIT_INCH_HG;
677 info->distance_unit = DISTANCE_UNIT_MILES;
678 }
679
680 const WeatherLocation *
weather_info_get_location(WeatherInfo * info)681 weather_info_get_location (WeatherInfo *info)
682 {
683 g_return_val_if_fail (info != NULL, NULL);
684 return info->location;
685 }
686
687 const gchar *
weather_info_get_location_name(WeatherInfo * info)688 weather_info_get_location_name (WeatherInfo *info)
689 {
690 g_return_val_if_fail (info != NULL, NULL);
691 g_return_val_if_fail (info->location != NULL, NULL);
692 return info->location->name;
693 }
694
695 const gchar *
weather_info_get_update(WeatherInfo * info)696 weather_info_get_update (WeatherInfo *info)
697 {
698 static gchar buf[200];
699 char *utf8, *timeformat;
700
701 g_return_val_if_fail (info != NULL, NULL);
702
703 if (!info->valid)
704 return "-";
705
706 if (info->update != 0) {
707 struct tm tm;
708 localtime_r (&info->update, &tm);
709 /* Translators: this is a format string for strftime
710 * see `man 3 strftime` for more details
711 */
712 timeformat = g_locale_from_utf8 (_("%a, %b %d / %H:%M"), -1,
713 NULL, NULL, NULL);
714 if (!timeformat) {
715 strcpy (buf, "???");
716 }
717 else if (strftime (buf, sizeof (buf), timeformat, &tm) <= 0) {
718 strcpy (buf, "???");
719 }
720 g_free (timeformat);
721
722 /* Convert to UTF-8 */
723 utf8 = g_locale_to_utf8 (buf, -1, NULL, NULL, NULL);
724 strcpy (buf, utf8);
725 g_free (utf8);
726 } else {
727 strncpy (buf, _("Unknown observation time"), sizeof (buf));
728 buf[sizeof (buf)-1] = '\0';
729 }
730
731 return buf;
732 }
733
734 const gchar *
weather_info_get_sky(WeatherInfo * info)735 weather_info_get_sky (WeatherInfo *info)
736 {
737 g_return_val_if_fail (info != NULL, NULL);
738 if (!info->valid)
739 return "-";
740 if (info->sky < 0)
741 return _("Unknown");
742 return weather_sky_string (info->sky);
743 }
744
745 const gchar *
weather_info_get_conditions(WeatherInfo * info)746 weather_info_get_conditions (WeatherInfo *info)
747 {
748 g_return_val_if_fail (info != NULL, NULL);
749 if (!info->valid)
750 return "-";
751 return weather_conditions_string (info->cond);
752 }
753
754 static const gchar *
temperature_string(gdouble temp,TempUnit to_unit,gboolean want_round)755 temperature_string (gdouble temp, TempUnit to_unit, gboolean want_round)
756 {
757 static gchar buf[100];
758
759 switch (to_unit) {
760 case TEMP_UNIT_FAHRENHEIT:
761 if (!want_round) {
762 /* Translators: This is the temperature in degrees Fahrenheit (\302\260 is U+00B0 DEGREE SIGN) */
763 g_snprintf (buf, sizeof (buf), _("%.1f \302\260F"), temp);
764 } else {
765 const int range_problem = FE_INVALID | FE_DIVBYZERO | FE_OVERFLOW | FE_UNDERFLOW;
766 gdouble temp_r;
767
768 feclearexcept(range_problem);
769 temp_r = round (temp);
770 if (fetestexcept(range_problem))
771 g_snprintf (buf, sizeof (buf), _("n/a"));
772 else
773 /* Translators: This is the temperature in degrees Fahrenheit (\302\260 is U+00B0 DEGREE SIGN) */
774 g_snprintf (buf, sizeof (buf), _("%d \302\260F"), (int)temp_r);
775 }
776 break;
777 case TEMP_UNIT_CENTIGRADE:
778 if (!want_round) {
779 /* Translators: This is the temperature in degrees Celsius (\302\260 is U+00B0 DEGREE SIGN) */
780 g_snprintf (buf, sizeof (buf), _("%.1f \302\260C"), TEMP_F_TO_C (temp));
781 } else {
782 const int range_problem = FE_INVALID | FE_DIVBYZERO | FE_OVERFLOW | FE_UNDERFLOW;
783 gdouble temp_r;
784
785 feclearexcept(range_problem);
786 temp_r = round (TEMP_F_TO_C (temp));
787 if (fetestexcept(range_problem))
788 g_snprintf (buf, sizeof (buf), _("n/a"));
789 else
790 /* Translators: This is the temperature in degrees Celsius (\302\260 is U+00B0 DEGREE SIGN) */
791 g_snprintf (buf, sizeof (buf), _("%d \302\260C"), (int)temp_r);
792 }
793 break;
794 case TEMP_UNIT_KELVIN:
795 if (!want_round) {
796 /* Translators: This is the temperature in kelvin */
797 g_snprintf (buf, sizeof (buf), _("%.1f K"), TEMP_F_TO_K (temp));
798 } else {
799 const int range_problem = FE_INVALID | FE_DIVBYZERO | FE_OVERFLOW | FE_UNDERFLOW;
800 gdouble temp_r;
801
802 feclearexcept(range_problem);
803 temp_r = round (TEMP_F_TO_K (temp));
804 if (fetestexcept(range_problem))
805 g_snprintf (buf, sizeof (buf), _("n/a"));
806 else
807 /* Translators: This is the temperature in kelvin */
808 g_snprintf (buf, sizeof (buf), _("%d K"), (int)temp_r);
809 }
810 break;
811
812 case TEMP_UNIT_INVALID:
813 case TEMP_UNIT_DEFAULT:
814 default:
815 g_warning ("Conversion to illegal temperature unit: %d", to_unit);
816 return _("Unknown");
817 }
818
819 return buf;
820 }
821
822 const gchar *
weather_info_get_temp(WeatherInfo * info)823 weather_info_get_temp (WeatherInfo *info)
824 {
825 g_return_val_if_fail (info != NULL, NULL);
826
827 if (!info->valid)
828 return "-";
829 if (info->temp < -500.0)
830 return _("Unknown");
831
832 return temperature_string (info->temp, info->temperature_unit, FALSE);
833 }
834
835 const gchar *
weather_info_get_temp_min(WeatherInfo * info)836 weather_info_get_temp_min (WeatherInfo *info)
837 {
838 g_return_val_if_fail (info != NULL, NULL);
839
840 if (!info->valid || !info->tempMinMaxValid)
841 return "-";
842 if (info->temp_min < -500.0)
843 return _("Unknown");
844
845 return temperature_string (info->temp_min, info->temperature_unit, FALSE);
846 }
847
848 const gchar *
weather_info_get_temp_max(WeatherInfo * info)849 weather_info_get_temp_max (WeatherInfo *info)
850 {
851 g_return_val_if_fail (info != NULL, NULL);
852
853 if (!info->valid || !info->tempMinMaxValid)
854 return "-";
855 if (info->temp_max < -500.0)
856 return _("Unknown");
857
858 return temperature_string (info->temp_max, info->temperature_unit, FALSE);
859 }
860
861 const gchar *
weather_info_get_dew(WeatherInfo * info)862 weather_info_get_dew (WeatherInfo *info)
863 {
864 g_return_val_if_fail (info != NULL, NULL);
865
866 if (!info->valid)
867 return "-";
868 if (info->dew < -500.0)
869 return _("Unknown");
870
871 return temperature_string (info->dew, info->temperature_unit, FALSE);
872 }
873
874 const gchar *
weather_info_get_humidity(WeatherInfo * info)875 weather_info_get_humidity (WeatherInfo *info)
876 {
877 static gchar buf[20];
878 gdouble humidity;
879
880 g_return_val_if_fail (info != NULL, NULL);
881
882 if (!info->valid)
883 return "-";
884
885 humidity = calc_humidity (info->temp, info->dew);
886 if (humidity < 0.0)
887 return _("Unknown");
888
889 /* Translators: This is the humidity in percent */
890 g_snprintf (buf, sizeof (buf), _("%.f%%"), humidity);
891 return buf;
892 }
893
894 const gchar *
weather_info_get_apparent(WeatherInfo * info)895 weather_info_get_apparent (WeatherInfo *info)
896 {
897 gdouble apparent;
898
899 g_return_val_if_fail (info != NULL, NULL);
900 if (!info->valid)
901 return "-";
902
903 apparent = calc_apparent (info);
904 if (apparent < -500.0)
905 return _("Unknown");
906
907 return temperature_string (apparent, info->temperature_unit, FALSE);
908 }
909
910 static const gchar *
windspeed_string(gfloat knots,SpeedUnit to_unit)911 windspeed_string (gfloat knots, SpeedUnit to_unit)
912 {
913 static gchar buf[100];
914
915 switch (to_unit) {
916 case SPEED_UNIT_KNOTS:
917 /* Translators: This is the wind speed in knots */
918 g_snprintf (buf, sizeof (buf), _("%0.1f knots"), knots);
919 break;
920 case SPEED_UNIT_MPH:
921 /* Translators: This is the wind speed in miles per hour */
922 g_snprintf (buf, sizeof (buf), _("%.1f mph"), WINDSPEED_KNOTS_TO_MPH (knots));
923 break;
924 case SPEED_UNIT_KPH:
925 /* Translators: This is the wind speed in kilometers per hour */
926 g_snprintf (buf, sizeof (buf), _("%.1f km/h"), WINDSPEED_KNOTS_TO_KPH (knots));
927 break;
928 case SPEED_UNIT_MS:
929 /* Translators: This is the wind speed in meters per second */
930 g_snprintf (buf, sizeof (buf), _("%.1f m/s"), WINDSPEED_KNOTS_TO_MS (knots));
931 break;
932 case SPEED_UNIT_BFT:
933 /* Translators: This is the wind speed as a Beaufort force factor
934 * (commonly used in nautical wind estimation).
935 */
936 g_snprintf (buf, sizeof (buf), _("Beaufort force %.1f"),
937 WINDSPEED_KNOTS_TO_BFT (knots));
938 break;
939 case SPEED_UNIT_INVALID:
940 case SPEED_UNIT_DEFAULT:
941 default:
942 g_warning ("Conversion to illegal speed unit: %d", to_unit);
943 return _("Unknown");
944 }
945
946 return buf;
947 }
948
949 const gchar *
weather_info_get_wind(WeatherInfo * info)950 weather_info_get_wind (WeatherInfo *info)
951 {
952 static gchar buf[200];
953
954 g_return_val_if_fail (info != NULL, NULL);
955
956 if (!info->valid)
957 return "-";
958 if (info->windspeed < 0.0 || info->wind < 0)
959 return _("Unknown");
960 if (info->windspeed == 0.00) {
961 strncpy (buf, _("Calm"), sizeof (buf));
962 buf[sizeof (buf)-1] = '\0';
963 } else {
964 /* Translators: This is 'wind direction' / 'wind speed' */
965 g_snprintf (buf, sizeof (buf), _("%s / %s"),
966 weather_wind_direction_string (info->wind),
967 windspeed_string (info->windspeed, info->speed_unit));
968 }
969 return buf;
970 }
971
972 const gchar *
weather_info_get_pressure(WeatherInfo * info)973 weather_info_get_pressure (WeatherInfo *info)
974 {
975 static gchar buf[100];
976
977 g_return_val_if_fail (info != NULL, NULL);
978
979 if (!info->valid)
980 return "-";
981 if (info->pressure < 0.0)
982 return _("Unknown");
983
984 switch (info->pressure_unit) {
985 case PRESSURE_UNIT_INCH_HG:
986 /* Translators: This is pressure in inches of mercury */
987 g_snprintf (buf, sizeof (buf), _("%.2f inHg"), info->pressure);
988 break;
989 case PRESSURE_UNIT_MM_HG:
990 /* Translators: This is pressure in millimeters of mercury */
991 g_snprintf (buf, sizeof (buf), _("%.1f mmHg"), PRESSURE_INCH_TO_MM (info->pressure));
992 break;
993 case PRESSURE_UNIT_KPA:
994 /* Translators: This is pressure in kiloPascals */
995 g_snprintf (buf, sizeof (buf), _("%.2f kPa"), PRESSURE_INCH_TO_KPA (info->pressure));
996 break;
997 case PRESSURE_UNIT_HPA:
998 /* Translators: This is pressure in hectoPascals */
999 g_snprintf (buf, sizeof (buf), _("%.2f hPa"), PRESSURE_INCH_TO_HPA (info->pressure));
1000 break;
1001 case PRESSURE_UNIT_MB:
1002 /* Translators: This is pressure in millibars */
1003 g_snprintf (buf, sizeof (buf), _("%.2f mb"), PRESSURE_INCH_TO_MB (info->pressure));
1004 break;
1005 case PRESSURE_UNIT_ATM:
1006 /* Translators: This is pressure in atmospheres */
1007 g_snprintf (buf, sizeof (buf), _("%.3f atm"), PRESSURE_INCH_TO_ATM (info->pressure));
1008 break;
1009
1010 case PRESSURE_UNIT_INVALID:
1011 case PRESSURE_UNIT_DEFAULT:
1012 default:
1013 g_warning ("Conversion to illegal pressure unit: %d", info->pressure_unit);
1014 return _("Unknown");
1015 }
1016
1017 return buf;
1018 }
1019
1020 const gchar *
weather_info_get_visibility(WeatherInfo * info)1021 weather_info_get_visibility (WeatherInfo *info)
1022 {
1023 static gchar buf[100];
1024
1025 g_return_val_if_fail (info != NULL, NULL);
1026
1027 if (!info->valid)
1028 return "-";
1029 if (info->visibility < 0.0)
1030 return _("Unknown");
1031
1032 switch (info->distance_unit) {
1033 case DISTANCE_UNIT_MILES:
1034 /* Translators: This is the visibility in miles */
1035 g_snprintf (buf, sizeof (buf), _("%.1f miles"), info->visibility);
1036 break;
1037 case DISTANCE_UNIT_KM:
1038 /* Translators: This is the visibility in kilometers */
1039 g_snprintf (buf, sizeof (buf), _("%.1f km"), VISIBILITY_SM_TO_KM (info->visibility));
1040 break;
1041 case DISTANCE_UNIT_METERS:
1042 /* Translators: This is the visibility in meters */
1043 g_snprintf (buf, sizeof (buf), _("%.0fm"), VISIBILITY_SM_TO_M (info->visibility));
1044 break;
1045
1046 case DISTANCE_UNIT_INVALID:
1047 case DISTANCE_UNIT_DEFAULT:
1048 default:
1049 g_warning ("Conversion to illegal visibility unit: %d", info->pressure_unit);
1050 return _("Unknown");
1051 }
1052
1053 return buf;
1054 }
1055
1056 const gchar *
weather_info_get_sunrise(WeatherInfo * info)1057 weather_info_get_sunrise (WeatherInfo *info)
1058 {
1059 static gchar buf[200];
1060 struct tm tm;
1061
1062 g_return_val_if_fail (info && info->location, NULL);
1063
1064 if (!info->location->latlon_valid)
1065 return "-";
1066 if (!info->valid)
1067 return "-";
1068 if (!calc_sun (info))
1069 return "-";
1070
1071 localtime_r (&info->sunrise, &tm);
1072 if (strftime (buf, sizeof (buf), _("%H:%M"), &tm) <= 0)
1073 return "-";
1074 return buf;
1075 }
1076
1077 const gchar *
weather_info_get_sunset(WeatherInfo * info)1078 weather_info_get_sunset (WeatherInfo *info)
1079 {
1080 static gchar buf[200];
1081 struct tm tm;
1082
1083 g_return_val_if_fail (info && info->location, NULL);
1084
1085 if (!info->location->latlon_valid)
1086 return "-";
1087 if (!info->valid)
1088 return "-";
1089 if (!calc_sun (info))
1090 return "-";
1091
1092 localtime_r (&info->sunset, &tm);
1093 if (strftime (buf, sizeof (buf), _("%H:%M"), &tm) <= 0)
1094 return "-";
1095 return buf;
1096 }
1097
1098 const gchar *
weather_info_get_forecast(WeatherInfo * info)1099 weather_info_get_forecast (WeatherInfo *info)
1100 {
1101 g_return_val_if_fail (info != NULL, NULL);
1102 return info->forecast;
1103 }
1104
1105 /**
1106 * weather_info_get_forecast_list:
1107 * Returns list of WeatherInfo* objects for the forecast.
1108 * The list is owned by the 'info' object thus is alive as long
1109 * as the 'info'. This list is filled only when requested with
1110 * type FORECAST_LIST and if available for given location.
1111 * The 'update' property is the date/time when the forecast info
1112 * is used for.
1113 **/
1114 GSList *
weather_info_get_forecast_list(WeatherInfo * info)1115 weather_info_get_forecast_list (WeatherInfo *info)
1116 {
1117 g_return_val_if_fail (info != NULL, NULL);
1118
1119 if (!info->valid)
1120 return NULL;
1121
1122 return info->forecast_list;
1123 }
1124
1125 GdkPixbufAnimation *
weather_info_get_radar(WeatherInfo * info)1126 weather_info_get_radar (WeatherInfo *info)
1127 {
1128 g_return_val_if_fail (info != NULL, NULL);
1129 return info->radar;
1130 }
1131
1132 const gchar *
weather_info_get_temp_summary(WeatherInfo * info)1133 weather_info_get_temp_summary (WeatherInfo *info)
1134 {
1135 g_return_val_if_fail (info != NULL, NULL);
1136
1137 if (!info->valid || info->temp < -500.0)
1138 return "--";
1139
1140 return temperature_string (info->temp, info->temperature_unit, TRUE);
1141
1142 }
1143
1144 gchar *
weather_info_get_weather_summary(WeatherInfo * info)1145 weather_info_get_weather_summary (WeatherInfo *info)
1146 {
1147 const gchar *buf;
1148
1149 g_return_val_if_fail (info != NULL, NULL);
1150
1151 if (!info->valid)
1152 return g_strdup (_("Retrieval failed"));
1153 buf = weather_info_get_conditions (info);
1154 if (!strcmp (buf, "-"))
1155 buf = weather_info_get_sky (info);
1156 return g_strdup_printf ("%s: %s", weather_info_get_location_name (info), buf);
1157 }
1158
1159 const gchar *
weather_info_get_icon_name(WeatherInfo * info)1160 weather_info_get_icon_name (WeatherInfo *info)
1161 {
1162 WeatherConditions cond;
1163 WeatherSky sky;
1164 time_t current_time;
1165 gboolean daytime;
1166 gchar* icon;
1167 static gchar icon_buffer[32];
1168 WeatherMoonPhase moonPhase;
1169 WeatherMoonLatitude moonLat;
1170 gint phase;
1171
1172 g_return_val_if_fail (info != NULL, NULL);
1173
1174 if (!info->valid)
1175 return NULL;
1176
1177 cond = info->cond;
1178 sky = info->sky;
1179
1180 if (cond.significant) {
1181 if (cond.phenomenon != PHENOMENON_NONE &&
1182 cond.qualifier == QUALIFIER_THUNDERSTORM)
1183 return "weather-storm";
1184
1185 switch (cond.phenomenon) {
1186 case PHENOMENON_INVALID:
1187 case PHENOMENON_LAST:
1188 case PHENOMENON_NONE:
1189 break;
1190
1191 case PHENOMENON_DRIZZLE:
1192 case PHENOMENON_RAIN:
1193 case PHENOMENON_UNKNOWN_PRECIPITATION:
1194 case PHENOMENON_HAIL:
1195 case PHENOMENON_SMALL_HAIL:
1196 return "weather-showers";
1197
1198 case PHENOMENON_SNOW:
1199 case PHENOMENON_SNOW_GRAINS:
1200 case PHENOMENON_ICE_PELLETS:
1201 case PHENOMENON_ICE_CRYSTALS:
1202 return "weather-snow";
1203
1204 case PHENOMENON_TORNADO:
1205 case PHENOMENON_SQUALL:
1206 return "weather-storm";
1207
1208 case PHENOMENON_MIST:
1209 case PHENOMENON_FOG:
1210 case PHENOMENON_SMOKE:
1211 case PHENOMENON_VOLCANIC_ASH:
1212 case PHENOMENON_SAND:
1213 case PHENOMENON_HAZE:
1214 case PHENOMENON_SPRAY:
1215 case PHENOMENON_DUST:
1216 case PHENOMENON_SANDSTORM:
1217 case PHENOMENON_DUSTSTORM:
1218 case PHENOMENON_FUNNEL_CLOUD:
1219 case PHENOMENON_DUST_WHIRLS:
1220 return "weather-fog";
1221 }
1222 }
1223
1224 if (info->midnightSun ||
1225 (!info->sunriseValid && !info->sunsetValid))
1226 daytime = TRUE;
1227 else if (info->polarNight)
1228 daytime = FALSE;
1229 else {
1230 current_time = time (NULL);
1231 daytime =
1232 ( !info->sunriseValid || (current_time >= info->sunrise) ) &&
1233 ( !info->sunsetValid || (current_time < info->sunset) );
1234 }
1235
1236 switch (sky) {
1237 case SKY_INVALID:
1238 case SKY_LAST:
1239 case SKY_CLEAR:
1240 if (daytime)
1241 return "weather-clear";
1242 else {
1243 icon = g_stpcpy(icon_buffer, "weather-clear-night");
1244 break;
1245 }
1246
1247 case SKY_BROKEN:
1248 case SKY_SCATTERED:
1249 case SKY_FEW:
1250 if (daytime)
1251 return "weather-few-clouds";
1252 else {
1253 icon = g_stpcpy(icon_buffer, "weather-few-clouds-night");
1254 break;
1255 }
1256
1257 case SKY_OVERCAST:
1258 return "weather-overcast";
1259
1260 default: /* unrecognized */
1261 return NULL;
1262 }
1263
1264 /*
1265 * A phase-of-moon icon is to be returned.
1266 * Determine which one based on the moon's location
1267 */
1268 if (info->moonValid && weather_info_get_value_moonphase(info, &moonPhase, &moonLat)) {
1269 phase = (gint)((moonPhase * MOON_PHASES / 360.) + 0.5);
1270 if (phase == MOON_PHASES) {
1271 phase = 0;
1272 } else if (phase > 0 &&
1273 (RADIANS_TO_DEGREES(weather_info_get_location(info)->latitude)
1274 < moonLat)) {
1275 /*
1276 * Locations south of the moon's latitude will see the moon in the
1277 * northern sky. The moon waxes and wanes from left to right
1278 * so we reference an icon running in the opposite direction.
1279 */
1280 phase = MOON_PHASES - phase;
1281 }
1282
1283 /*
1284 * If the moon is not full then append the angle to the icon string.
1285 * Note that an icon by this name is not required to exist:
1286 * the caller can use GTK_ICON_LOOKUP_GENERIC_FALLBACK to fall back to
1287 * the full moon image.
1288 */
1289 if ((0 == (MOON_PHASES & 0x1)) && (MOON_PHASES/2 != phase)) {
1290 g_snprintf(icon, sizeof(icon_buffer) - strlen(icon_buffer),
1291 "-%03d", phase * 360 / MOON_PHASES);
1292 }
1293 }
1294 return icon_buffer;
1295 }
1296
1297 static gboolean
temperature_value(gdouble temp_f,TempUnit to_unit,gdouble * value,TempUnit def_unit)1298 temperature_value (gdouble temp_f,
1299 TempUnit to_unit,
1300 gdouble *value,
1301 TempUnit def_unit)
1302 {
1303 gboolean ok = TRUE;
1304
1305 *value = 0.0;
1306 if (temp_f < -500.0)
1307 return FALSE;
1308
1309 if (to_unit == TEMP_UNIT_DEFAULT)
1310 to_unit = def_unit;
1311
1312 switch (to_unit) {
1313 case TEMP_UNIT_FAHRENHEIT:
1314 *value = temp_f;
1315 break;
1316 case TEMP_UNIT_CENTIGRADE:
1317 *value = TEMP_F_TO_C (temp_f);
1318 break;
1319 case TEMP_UNIT_KELVIN:
1320 *value = TEMP_F_TO_K (temp_f);
1321 break;
1322 case TEMP_UNIT_INVALID:
1323 case TEMP_UNIT_DEFAULT:
1324 default:
1325 ok = FALSE;
1326 break;
1327 }
1328
1329 return ok;
1330 }
1331
1332 static gboolean
speed_value(gdouble knots,SpeedUnit to_unit,gdouble * value,SpeedUnit def_unit)1333 speed_value (gdouble knots, SpeedUnit to_unit, gdouble *value, SpeedUnit def_unit)
1334 {
1335 gboolean ok = TRUE;
1336
1337 *value = -1.0;
1338
1339 if (knots < 0.0)
1340 return FALSE;
1341
1342 if (to_unit == SPEED_UNIT_DEFAULT)
1343 to_unit = def_unit;
1344
1345 switch (to_unit) {
1346 case SPEED_UNIT_KNOTS:
1347 *value = knots;
1348 break;
1349 case SPEED_UNIT_MPH:
1350 *value = WINDSPEED_KNOTS_TO_MPH (knots);
1351 break;
1352 case SPEED_UNIT_KPH:
1353 *value = WINDSPEED_KNOTS_TO_KPH (knots);
1354 break;
1355 case SPEED_UNIT_MS:
1356 *value = WINDSPEED_KNOTS_TO_MS (knots);
1357 break;
1358 case SPEED_UNIT_BFT:
1359 *value = WINDSPEED_KNOTS_TO_BFT (knots);
1360 break;
1361 case SPEED_UNIT_INVALID:
1362 case SPEED_UNIT_DEFAULT:
1363 default:
1364 ok = FALSE;
1365 break;
1366 }
1367
1368 return ok;
1369 }
1370
1371 static gboolean
pressure_value(gdouble inHg,PressureUnit to_unit,gdouble * value,PressureUnit def_unit)1372 pressure_value (gdouble inHg, PressureUnit to_unit, gdouble *value, PressureUnit def_unit)
1373 {
1374 gboolean ok = TRUE;
1375
1376 *value = -1.0;
1377
1378 if (inHg < 0.0)
1379 return FALSE;
1380
1381 if (to_unit == PRESSURE_UNIT_DEFAULT)
1382 to_unit = def_unit;
1383
1384 switch (to_unit) {
1385 case PRESSURE_UNIT_INCH_HG:
1386 *value = inHg;
1387 break;
1388 case PRESSURE_UNIT_MM_HG:
1389 *value = PRESSURE_INCH_TO_MM (inHg);
1390 break;
1391 case PRESSURE_UNIT_KPA:
1392 *value = PRESSURE_INCH_TO_KPA (inHg);
1393 break;
1394 case PRESSURE_UNIT_HPA:
1395 *value = PRESSURE_INCH_TO_HPA (inHg);
1396 break;
1397 case PRESSURE_UNIT_MB:
1398 *value = PRESSURE_INCH_TO_MB (inHg);
1399 break;
1400 case PRESSURE_UNIT_ATM:
1401 *value = PRESSURE_INCH_TO_ATM (inHg);
1402 break;
1403 case PRESSURE_UNIT_INVALID:
1404 case PRESSURE_UNIT_DEFAULT:
1405 default:
1406 ok = FALSE;
1407 break;
1408 }
1409
1410 return ok;
1411 }
1412
1413 static gboolean
distance_value(gdouble miles,DistanceUnit to_unit,gdouble * value,DistanceUnit def_unit)1414 distance_value (gdouble miles, DistanceUnit to_unit, gdouble *value, DistanceUnit def_unit)
1415 {
1416 gboolean ok = TRUE;
1417
1418 *value = -1.0;
1419
1420 if (miles < 0.0)
1421 return FALSE;
1422
1423 if (to_unit == DISTANCE_UNIT_DEFAULT)
1424 to_unit = def_unit;
1425
1426 switch (to_unit) {
1427 case DISTANCE_UNIT_MILES:
1428 *value = miles;
1429 break;
1430 case DISTANCE_UNIT_KM:
1431 *value = VISIBILITY_SM_TO_KM (miles);
1432 break;
1433 case DISTANCE_UNIT_METERS:
1434 *value = VISIBILITY_SM_TO_M (miles);
1435 break;
1436 case DISTANCE_UNIT_INVALID:
1437 case DISTANCE_UNIT_DEFAULT:
1438 default:
1439 ok = FALSE;
1440 break;
1441 }
1442
1443 return ok;
1444 }
1445
1446 gboolean
weather_info_get_value_sky(WeatherInfo * info,WeatherSky * sky)1447 weather_info_get_value_sky (WeatherInfo *info, WeatherSky *sky)
1448 {
1449 g_return_val_if_fail (info != NULL, FALSE);
1450 g_return_val_if_fail (sky != NULL, FALSE);
1451
1452 if (!info->valid)
1453 return FALSE;
1454
1455 if (info->sky <= SKY_INVALID || info->sky >= SKY_LAST)
1456 return FALSE;
1457
1458 *sky = info->sky;
1459
1460 return TRUE;
1461 }
1462
1463 gboolean
weather_info_get_value_conditions(WeatherInfo * info,WeatherConditionPhenomenon * phenomenon,WeatherConditionQualifier * qualifier)1464 weather_info_get_value_conditions (WeatherInfo *info, WeatherConditionPhenomenon *phenomenon, WeatherConditionQualifier *qualifier)
1465 {
1466 g_return_val_if_fail (info != NULL, FALSE);
1467 g_return_val_if_fail (phenomenon != NULL, FALSE);
1468 g_return_val_if_fail (qualifier != NULL, FALSE);
1469
1470 if (!info->valid)
1471 return FALSE;
1472
1473 if (!info->cond.significant)
1474 return FALSE;
1475
1476 if (!(info->cond.phenomenon > PHENOMENON_INVALID &&
1477 info->cond.phenomenon < PHENOMENON_LAST &&
1478 info->cond.qualifier > QUALIFIER_INVALID &&
1479 info->cond.qualifier < QUALIFIER_LAST))
1480 return FALSE;
1481
1482 *phenomenon = info->cond.phenomenon;
1483 *qualifier = info->cond.qualifier;
1484
1485 return TRUE;
1486 }
1487
1488 gboolean
weather_info_get_value_temp(WeatherInfo * info,TempUnit unit,gdouble * value)1489 weather_info_get_value_temp (WeatherInfo *info, TempUnit unit, gdouble *value)
1490 {
1491 g_return_val_if_fail (info != NULL, FALSE);
1492 g_return_val_if_fail (value != NULL, FALSE);
1493
1494 if (!info->valid)
1495 return FALSE;
1496
1497 return temperature_value (info->temp, unit, value, info->temperature_unit);
1498 }
1499
1500 gboolean
weather_info_get_value_temp_min(WeatherInfo * info,TempUnit unit,gdouble * value)1501 weather_info_get_value_temp_min (WeatherInfo *info, TempUnit unit, gdouble *value)
1502 {
1503 g_return_val_if_fail (info != NULL, FALSE);
1504 g_return_val_if_fail (value != NULL, FALSE);
1505
1506 if (!info->valid || !info->tempMinMaxValid)
1507 return FALSE;
1508
1509 return temperature_value (info->temp_min, unit, value, info->temperature_unit);
1510 }
1511
1512 gboolean
weather_info_get_value_temp_max(WeatherInfo * info,TempUnit unit,gdouble * value)1513 weather_info_get_value_temp_max (WeatherInfo *info, TempUnit unit, gdouble *value)
1514 {
1515 g_return_val_if_fail (info != NULL, FALSE);
1516 g_return_val_if_fail (value != NULL, FALSE);
1517
1518 if (!info->valid || !info->tempMinMaxValid)
1519 return FALSE;
1520
1521 return temperature_value (info->temp_max, unit, value, info->temperature_unit);
1522 }
1523
1524 gboolean
weather_info_get_value_dew(WeatherInfo * info,TempUnit unit,gdouble * value)1525 weather_info_get_value_dew (WeatherInfo *info, TempUnit unit, gdouble *value)
1526 {
1527 g_return_val_if_fail (info != NULL, FALSE);
1528 g_return_val_if_fail (value != NULL, FALSE);
1529
1530 if (!info->valid)
1531 return FALSE;
1532
1533 return temperature_value (info->dew, unit, value, info->temperature_unit);
1534 }
1535
1536 gboolean
weather_info_get_value_apparent(WeatherInfo * info,TempUnit unit,gdouble * value)1537 weather_info_get_value_apparent (WeatherInfo *info, TempUnit unit, gdouble *value)
1538 {
1539 g_return_val_if_fail (info != NULL, FALSE);
1540 g_return_val_if_fail (value != NULL, FALSE);
1541
1542 if (!info->valid)
1543 return FALSE;
1544
1545 return temperature_value (calc_apparent (info), unit, value, info->temperature_unit);
1546 }
1547
1548 gboolean
weather_info_get_value_update(WeatherInfo * info,time_t * value)1549 weather_info_get_value_update (WeatherInfo *info, time_t *value)
1550 {
1551 g_return_val_if_fail (info != NULL, FALSE);
1552 g_return_val_if_fail (value != NULL, FALSE);
1553
1554 if (!info->valid)
1555 return FALSE;
1556
1557 *value = info->update;
1558
1559 return TRUE;
1560 }
1561
1562 gboolean
weather_info_get_value_sunrise(WeatherInfo * info,time_t * value)1563 weather_info_get_value_sunrise (WeatherInfo *info, time_t *value)
1564 {
1565 g_return_val_if_fail (info != NULL, FALSE);
1566 g_return_val_if_fail (value != NULL, FALSE);
1567
1568 if (!info->valid || !info->sunriseValid)
1569 return FALSE;
1570
1571 *value = info->sunrise;
1572
1573 return TRUE;
1574 }
1575
1576 gboolean
weather_info_get_value_sunset(WeatherInfo * info,time_t * value)1577 weather_info_get_value_sunset (WeatherInfo *info, time_t *value)
1578 {
1579 g_return_val_if_fail (info != NULL, FALSE);
1580 g_return_val_if_fail (value != NULL, FALSE);
1581
1582 if (!info->valid || !info->sunsetValid)
1583 return FALSE;
1584
1585 *value = info->sunset;
1586
1587 return TRUE;
1588 }
1589
1590 gboolean
weather_info_get_value_moonphase(WeatherInfo * info,WeatherMoonPhase * value,WeatherMoonLatitude * lat)1591 weather_info_get_value_moonphase (WeatherInfo *info,
1592 WeatherMoonPhase *value,
1593 WeatherMoonLatitude *lat)
1594 {
1595 g_return_val_if_fail (info != NULL, FALSE);
1596 g_return_val_if_fail (value != NULL, FALSE);
1597
1598 if (!info->valid || !info->moonValid)
1599 return FALSE;
1600
1601 *value = info->moonphase;
1602 *lat = info->moonlatitude;
1603
1604 return TRUE;
1605 }
1606
1607 gboolean
weather_info_get_value_wind(WeatherInfo * info,SpeedUnit unit,gdouble * speed,WeatherWindDirection * direction)1608 weather_info_get_value_wind (WeatherInfo *info, SpeedUnit unit, gdouble *speed, WeatherWindDirection *direction)
1609 {
1610 gboolean res = FALSE;
1611
1612 g_return_val_if_fail (info != NULL, FALSE);
1613 g_return_val_if_fail (speed != NULL, FALSE);
1614 g_return_val_if_fail (direction != NULL, FALSE);
1615
1616 if (!info->valid)
1617 return FALSE;
1618
1619 if (info->windspeed < 0.0 || info->wind <= WIND_INVALID || info->wind >= WIND_LAST)
1620 return FALSE;
1621
1622 res = speed_value (info->windspeed, unit, speed, info->speed_unit);
1623 *direction = info->wind;
1624
1625 return res;
1626 }
1627
1628 gboolean
weather_info_get_value_pressure(WeatherInfo * info,PressureUnit unit,gdouble * value)1629 weather_info_get_value_pressure (WeatherInfo *info, PressureUnit unit, gdouble *value)
1630 {
1631 g_return_val_if_fail (info != NULL, FALSE);
1632 g_return_val_if_fail (value != NULL, FALSE);
1633
1634 if (!info->valid)
1635 return FALSE;
1636
1637 return pressure_value (info->pressure, unit, value, info->pressure_unit);
1638 }
1639
1640 gboolean
weather_info_get_value_visibility(WeatherInfo * info,DistanceUnit unit,gdouble * value)1641 weather_info_get_value_visibility (WeatherInfo *info, DistanceUnit unit, gdouble *value)
1642 {
1643 g_return_val_if_fail (info != NULL, FALSE);
1644 g_return_val_if_fail (value != NULL, FALSE);
1645
1646 if (!info->valid)
1647 return FALSE;
1648
1649 return distance_value (info->visibility, unit, value, info->distance_unit);
1650 }
1651
1652 /**
1653 * weather_info_get_upcoming_moonphases:
1654 * @info: WeatherInfo containing the time_t of interest
1655 * @phases: An array of four time_t values that will hold the returned values.
1656 * The values are estimates of the time of the next new, quarter, full and
1657 * three-quarter moons.
1658 *
1659 * Returns: gboolean indicating success or failure
1660 */
1661 gboolean
weather_info_get_upcoming_moonphases(WeatherInfo * info,time_t * phases)1662 weather_info_get_upcoming_moonphases (WeatherInfo *info, time_t *phases)
1663 {
1664 g_return_val_if_fail (info != NULL, FALSE);
1665 g_return_val_if_fail (phases != NULL, FALSE);
1666
1667 return calc_moon_phases(info, phases);
1668 }
1669
1670 static void
_weather_internal_check(void)1671 _weather_internal_check (void)
1672 {
1673 g_assert (G_N_ELEMENTS (wind_direction_str) == WIND_LAST);
1674 g_assert (G_N_ELEMENTS (sky_str) == SKY_LAST);
1675 g_assert (G_N_ELEMENTS (conditions_str) == PHENOMENON_LAST);
1676 g_assert (G_N_ELEMENTS (conditions_str[0]) == QUALIFIER_LAST);
1677 }
1678