~ubuntu-branches/ubuntu/quantal/evolution-data-server/quantal

« back to all changes in this revision

Viewing changes to .pc/gweather_3.6.patch/calendar/backends/weather/e-weather-source-ccf.c

  • Committer: Package Import Robot
  • Author(s): Robert Ancell
  • Date: 2012-07-05 11:06:20 UTC
  • Revision ID: package-import@ubuntu.com-20120705110620-cgh20ouc7hte0ouf
Tags: 3.5.3.1-0ubuntu2
* debian/control:
  - Bump build-depends on libgweather-3-dev
* debian/patches/gweather_3.6.patch:
  - Use libgweather 3.6

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Evolution calendar - weather backend source class for parsing
 
2
 *      CCF (coded cities forecast) formatted NWS reports
 
3
 *
 
4
 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
 
5
 *
 
6
 * Authors: David Trowbridge <trowbrds@cs.colorado.edu>
 
7
 *
 
8
 * This program is free software; you can redistribute it and/or
 
9
 * modify it under the terms of version 2 of the GNU Lesser General Public
 
10
 * License as published by the Free Software Foundation.
 
11
 *
 
12
 * This program is distributed in the hope that it will be useful,
 
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
15
 * GNU Lesser General Public License for more details.
 
16
 *
 
17
 * You should have received a copy of the GNU Lesser General Public License
 
18
 * along with this program; if not, write to the Free Software
 
19
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 
20
 */
 
21
 
 
22
#ifdef HAVE_CONFIG_H
 
23
#include <config.h>
 
24
#endif
 
25
 
 
26
#include <string.h>
 
27
#include <stdlib.h>
 
28
 
 
29
#include <glib/gi18n-lib.h>
 
30
 
 
31
#include "e-weather-source-ccf.h"
 
32
 
 
33
#define GWEATHER_I_KNOW_THIS_IS_UNSTABLE
 
34
#include <libgweather/weather.h>
 
35
#include <libgweather/gweather-xml.h>
 
36
#undef GWEATHER_I_KNOW_THIS_IS_UNSTABLE
 
37
 
 
38
#ifdef G_OS_WIN32
 
39
 
 
40
#ifdef localtime_r
 
41
#undef localtime_r
 
42
#endif
 
43
 
 
44
/* The localtime() in Microsoft's C library is MT-safe */
 
45
#define localtime_r(tp,tmp) (localtime(tp)?(*(tmp)=*localtime(tp),(tmp)):0)
 
46
 
 
47
/* strtok() is also MT-safe (but not stateless, still uses only one
 
48
 * buffer pointer per thread, but for the use of strtok_r() here
 
49
 * that's enough).
 
50
 */
 
51
#define strtok_r(s,sep,lasts) (*(lasts)=strtok((s),(sep)))
 
52
#endif
 
53
 
 
54
G_DEFINE_TYPE (EWeatherSourceCCF, e_weather_source_ccf, E_TYPE_WEATHER_SOURCE)
 
55
 
 
56
struct search_struct
 
57
{
 
58
        const gchar *code;
 
59
        const gchar *name;
 
60
        gboolean is_old;
 
61
        WeatherLocation *location;
 
62
};
 
63
 
 
64
static gboolean
 
65
find_location_func (GtkTreeModel *model,
 
66
                    GtkTreePath *path,
 
67
                    GtkTreeIter *node,
 
68
                    gpointer data)
 
69
{
 
70
        WeatherLocation *wl = NULL;
 
71
        struct search_struct *search = (struct search_struct *) data;
 
72
 
 
73
        gtk_tree_model_get (model, node, GWEATHER_XML_COL_POINTER, &wl, -1);
 
74
        if (!wl || !wl->name || !wl->code || !search || search->location)
 
75
                return FALSE;
 
76
 
 
77
        if (((!strcmp (wl->code, search->code)) || (search->is_old && !strcmp (wl->code + 1, search->code))) &&
 
78
             (!strcmp (wl->name, search->name))) {
 
79
                search->location = weather_location_clone (wl);
 
80
                return TRUE;
 
81
        }
 
82
 
 
83
        return FALSE;
 
84
}
 
85
 
 
86
static WeatherLocation *
 
87
find_location (const gchar *code_name,
 
88
               gboolean is_old)
 
89
{
 
90
        GtkTreeModel *model;
 
91
        gchar **ids;
 
92
        struct search_struct search;
 
93
 
 
94
        search.location = NULL;
 
95
 
 
96
        ids = g_strsplit (code_name, "/", 2);
 
97
 
 
98
        if (!ids || !ids[0] || !ids[1])
 
99
                goto done;
 
100
 
 
101
        model = gweather_xml_load_locations ();
 
102
        if (!model)
 
103
                goto done;
 
104
 
 
105
        search.code = ids[0];
 
106
        search.name = ids[1];
 
107
        search.is_old = is_old;
 
108
        search.location = NULL;
 
109
 
 
110
        gtk_tree_model_foreach (model, (GtkTreeModelForeachFunc) find_location_func, &search);
 
111
 
 
112
        gweather_xml_free_locations (model);
 
113
        g_strfreev (ids);
 
114
 
 
115
done:
 
116
        return search.location;
 
117
}
 
118
 
 
119
#if 0
 
120
static GSList *
 
121
tokenize (gchar *buffer)
 
122
{
 
123
        gchar *token;
 
124
        gchar *tokbuf;
 
125
        GSList *ret;
 
126
 
 
127
        token = strtok_r (buffer, " \n", &tokbuf);
 
128
        ret = g_slist_append (NULL, g_strdup (token));
 
129
        while ((token = strtok_r (NULL, " \n/", &tokbuf)))
 
130
                ret = g_slist_append (ret, g_strdup (token));
 
131
        return ret;
 
132
}
 
133
 
 
134
static void
 
135
date2tm (gchar *date,
 
136
         struct tm *times)
 
137
{
 
138
        gchar tmp[3];
 
139
        time_t curtime = time (NULL);
 
140
        tmp[2] = '\0';
 
141
 
 
142
        localtime_r (&curtime, times);
 
143
 
 
144
        tmp[0] = date[0]; tmp[1] = date[1];
 
145
        times->tm_mday = atoi (tmp);
 
146
        tmp[0] = date[2]; tmp[1] = date[3];
 
147
        times->tm_hour = atoi (tmp);
 
148
        tmp[0] = date[4]; tmp[1] = date[5];
 
149
        times->tm_min = atoi (tmp);
 
150
}
 
151
 
 
152
static WeatherConditions
 
153
decodeConditions (gchar code)
 
154
{
 
155
        switch (code) {
 
156
                case 'A': return WEATHER_FAIR;
 
157
                case 'B': return WEATHER_PARTLY_CLOUDY;
 
158
                case 'C': return WEATHER_CLOUDY;
 
159
                case 'D': return WEATHER_DUST;
 
160
                case 'E': return WEATHER_MOSTLY_CLOUDY;
 
161
                case 'F': return WEATHER_FOGGY;
 
162
                case 'G': return WEATHER_VERY_HOT_OR_HOT_HUMID;
 
163
                case 'H': return WEATHER_HAZE;
 
164
                case 'I': return WEATHER_VERY_COLD_WIND_CHILL;
 
165
                case 'J': return WEATHER_SNOW_SHOWERS;
 
166
                case 'K': return WEATHER_SMOKE;
 
167
                case 'L': return WEATHER_DRIZZLE;
 
168
                case 'M': return WEATHER_SNOW_SHOWERS;
 
169
                case 'N': return WEATHER_WINDY;
 
170
                case 'O': return WEATHER_RAIN_OR_SNOW_MIXED;
 
171
                case 'P': return WEATHER_BLIZZARD;
 
172
                case 'Q': return WEATHER_BLOWING_SNOW;
 
173
                case 'R': return WEATHER_RAIN;
 
174
                case 'S': return WEATHER_SNOW;
 
175
                case 'T': return WEATHER_THUNDERSTORMS;
 
176
                case 'U': return WEATHER_SUNNY;
 
177
                case 'V': return WEATHER_CLEAR;
 
178
                case 'W': return WEATHER_RAIN_SHOWERS;
 
179
                case 'X': return WEATHER_SLEET;
 
180
                case 'Y': return WEATHER_FREEZING_RAIN;
 
181
                case 'Z': return WEATHER_FREEZING_DRIZZLE;
 
182
                /* hmm, this should never happen. */
 
183
                default: return WEATHER_SUNNY;
 
184
        }
 
185
}
 
186
 
 
187
static gint
 
188
decodePOP (gchar data)
 
189
{
 
190
        gint ret;
 
191
 
 
192
        switch (data) {
 
193
                case '-':
 
194
                        ret = 5;
 
195
                        break;
 
196
                case '+':
 
197
                        ret = 95;
 
198
                        break;
 
199
                case '/':
 
200
                        ret = -1;       /* missing data */
 
201
                        break;
 
202
                default:
 
203
                        ret = (data - '0') * 10;
 
204
        }
 
205
        return ret;
 
206
}
 
207
 
 
208
static void
 
209
decodeSnowfall (gchar *data,
 
210
                gfloat *low,
 
211
                gfloat *high)
 
212
{
 
213
        gchar num[3];
 
214
        num[2] = '\0';
 
215
 
 
216
        num[0] = data[0]; num[1] = data[1];
 
217
        *low = atof (num) * 2.54f;
 
218
        num[0] = data[2]; num[1] = data[3];
 
219
        *high = atof (num) * 2.54f;
 
220
}
 
221
 
 
222
static float
 
223
ftoc (gchar *data)
 
224
{
 
225
        gint fahrenheit = atoi (data);
 
226
        if (fahrenheit >= 900)
 
227
                fahrenheit = (fahrenheit - 900) * -1;
 
228
        return ((gfloat)(fahrenheit - 32)) * 5.0f / 9.0f;
 
229
}
 
230
 
 
231
static void
 
232
e_weather_source_ccf_do_parse (EWeatherSourceCCF *source,
 
233
                               gchar *buffer)
 
234
{
 
235
        /* CCF gives us either 2 or 7 days of forecast data. IFPS WFO's
 
236
         * will produce 7 day forecasts, whereas pre-IFPS WFO's are only
 
237
         * mandated 2 (but may do 7). The morning forecast will give us either 2
 
238
         * or 7 days worth of data. The evening forecast will give us the evening's
 
239
         * low temperature plus 2 or 7 days forecast.
 
240
         *
 
241
         * The CCF format is described in NWS directive 10-503, but it's usually
 
242
         * easier to look at a summary put up by one of the stations:
 
243
         * http://www.crh.noaa.gov/lmk/product_guide/products/forecast/ccf.htm
 
244
         */
 
245
        WeatherForecast *forecasts = g_new0 (WeatherForecast, 7);
 
246
        GSList *tokens = tokenize (buffer);
 
247
        GSList *date;
 
248
        GSList *current = tokens;
 
249
        GList *fc = NULL;
 
250
        struct tm tms;
 
251
        gint i;
 
252
        time_t base;
 
253
        gint n;
 
254
 
 
255
        date = g_slist_nth (tokens, 3);
 
256
        date2tm (date->data, &tms);
 
257
 
 
258
        /* fast-forward to the particular station we're interested in */
 
259
        current = g_slist_nth (tokens, 5);
 
260
        while (strcmp (current->data, source->substation))
 
261
                current = g_slist_next (current);
 
262
        current = g_slist_next (current);
 
263
        /* pick up the first two conditions reports */
 
264
        forecasts[0].conditions = decodeConditions (((gchar *)(current->data))[0]);
 
265
        forecasts[1].conditions = decodeConditions (((gchar *)(current->data))[1]);
 
266
 
 
267
        current = g_slist_next (current);
 
268
        if (tms.tm_hour < 12) {
 
269
                for (i = 0; i < 2; i++) {
 
270
                        forecasts[i].high = ftoc (current->data);
 
271
                        current = g_slist_next (current);
 
272
                        forecasts[i].low  = ftoc (current->data);
 
273
                        current = g_slist_next (current);
 
274
                }
 
275
                forecasts[2].high = ftoc (current->data);
 
276
                current = g_slist_next (current);
 
277
                forecasts[0].pop = decodePOP (((gchar *)(current->data))[2]);
 
278
                forecasts[1].pop = decodePOP (((gchar *)(current->data))[4]);
 
279
        } else {
 
280
                for (i = 0; i < 2; i++) {
 
281
                        current = g_slist_next (current);
 
282
                        forecasts[i].high = ftoc (current->data);
 
283
                        current = g_slist_next (current);
 
284
                        forecasts[i].low  = ftoc (current->data);
 
285
                }
 
286
                current = g_slist_next (current);
 
287
                forecasts[0].pop = decodePOP (((gchar *)(current->data))[1]);
 
288
                forecasts[1].pop = decodePOP (((gchar *)(current->data))[3]);
 
289
        }
 
290
 
 
291
        current = g_slist_next (current);
 
292
        if (strlen (current->data) == 4) {
 
293
                /* we've got the optional snowfall field */
 
294
                if (tms.tm_hour < 12) {
 
295
                        decodeSnowfall (current->data, &forecasts[0].low, &forecasts[0].high);
 
296
                        current = g_slist_next (g_slist_next (current));
 
297
                        decodeSnowfall (current->data, &forecasts[1].low, &forecasts[1].high);
 
298
                } else {
 
299
                        current = g_slist_next (current);
 
300
                        decodeSnowfall (current->data, &forecasts[0].low, &forecasts[0].high);
 
301
                }
 
302
                current = g_slist_next (current);
 
303
        }
 
304
 
 
305
        /* set dates */
 
306
        base = mktime (&tms);
 
307
        if (tms.tm_hour >= 12)
 
308
                base += 43200;
 
309
        for (i = 0; i < 7; i++)
 
310
                forecasts[i].date = base + 86400 * i;
 
311
 
 
312
        if (current == NULL || strlen (current->data) == 3) {
 
313
                /* We've got a pre-IFPS station. Realloc and return */
 
314
                WeatherForecast *f = g_new0 (WeatherForecast, 2);
 
315
                memcpy (f, forecasts, sizeof (WeatherForecast) * 2);
 
316
                fc = g_list_append (fc, &f[0]);
 
317
                fc = g_list_append (fc, &f[1]);
 
318
                source->done (fc, source->finished_data);
 
319
        }
 
320
 
 
321
        /* Grab the conditions for the next 5 days */
 
322
        forecasts[2].conditions = decodeConditions (((gchar *)(current->data))[0]);
 
323
        forecasts[3].conditions = decodeConditions (((gchar *)(current->data))[1]);
 
324
        forecasts[4].conditions = decodeConditions (((gchar *)(current->data))[2]);
 
325
        forecasts[5].conditions = decodeConditions (((gchar *)(current->data))[3]);
 
326
        forecasts[6].conditions = decodeConditions (((gchar *)(current->data))[4]);
 
327
 
 
328
        /* Temperature forecasts */
 
329
        current = g_slist_next (current);
 
330
        if (tms.tm_hour < 12) {
 
331
                forecasts[2].low  = ftoc (current->data);
 
332
                for  (i = 3; i < 6; i++) {
 
333
                        current = g_slist_next (current);
 
334
                        forecasts[i].high = ftoc (current->data);
 
335
                        current = g_slist_next (current);
 
336
                        forecasts[i].low  = ftoc (current->data);
 
337
                }
 
338
                current = g_slist_next (current);
 
339
                forecasts[6].high = ftoc (current->data);
 
340
                forecasts[6].low  = forecasts[6].high;
 
341
                current = g_slist_next (current);
 
342
                forecasts[2].pop = decodePOP (((gchar *)(current->data))[1]);
 
343
                forecasts[3].pop = decodePOP (((gchar *)(current->data))[3]);
 
344
                forecasts[4].pop = decodePOP (((gchar *)(current->data))[5]);
 
345
                forecasts[5].pop = decodePOP (((gchar *)(current->data))[7]);
 
346
                forecasts[6].pop = decodePOP (((gchar *)(current->data))[9]);
 
347
                n = 7;
 
348
        } else {
 
349
                for (i = 2; i < 6; i++) {
 
350
                        forecasts[i].high = ftoc (current->data);
 
351
                        current = g_slist_next (current);
 
352
                        forecasts[i].low  = ftoc (current->data);
 
353
                        current = g_slist_next (current);
 
354
                }
 
355
                n = 6;
 
356
                /* hack for people who put out bad data, like Pueblo, CO. Yes, PUB, that means you */
 
357
                if (strlen (current->data) == 3)
 
358
                        current = g_slist_next (current);
 
359
                forecasts[1].pop = decodePOP (((gchar *)(current->data))[0]);
 
360
                forecasts[2].pop = decodePOP (((gchar *)(current->data))[2]);
 
361
                forecasts[3].pop = decodePOP (((gchar *)(current->data))[4]);
 
362
                forecasts[4].pop = decodePOP (((gchar *)(current->data))[6]);
 
363
                forecasts[5].pop = decodePOP (((gchar *)(current->data))[8]);
 
364
        }
 
365
 
 
366
        for (i = 0; i < n; i++) {
 
367
                fc = g_list_append (fc, &forecasts[i]);
 
368
        }
 
369
        source->done (fc, source->finished_data);
 
370
 
 
371
        g_free (forecasts);
 
372
        g_list_free (fc);
 
373
}
 
374
#endif
 
375
 
 
376
static void
 
377
parse_done (WeatherInfo *info,
 
378
            gpointer data)
 
379
{
 
380
        EWeatherSourceCCF *ccfsource = (EWeatherSourceCCF *) data;
 
381
 
 
382
        if (!ccfsource)
 
383
                return;
 
384
 
 
385
        if (!info || !weather_info_is_valid (info)) {
 
386
                ccfsource->done (NULL, ccfsource->finished_data);
 
387
                return;
 
388
        }
 
389
 
 
390
        ccfsource->done (info, ccfsource->finished_data);
 
391
}
 
392
 
 
393
static void
 
394
e_weather_source_ccf_parse (EWeatherSource *source,
 
395
                            EWeatherSourceFinished done,
 
396
                            gpointer data)
 
397
{
 
398
        EWeatherSourceCCF *ccfsource = (EWeatherSourceCCF *) source;
 
399
        WeatherPrefs prefs;
 
400
 
 
401
        ccfsource->finished_data = data;
 
402
        ccfsource->done = done;
 
403
 
 
404
        prefs.type = FORECAST_LIST;
 
405
        prefs.radar = FALSE;
 
406
        prefs.radar_custom_url = NULL;
 
407
        prefs.temperature_unit = TEMP_UNIT_CENTIGRADE;
 
408
        prefs.speed_unit = SPEED_UNIT_MS;
 
409
        prefs.pressure_unit = PRESSURE_UNIT_HPA;
 
410
        prefs.distance_unit = DISTANCE_UNIT_METERS;
 
411
 
 
412
        if (ccfsource->location && !ccfsource->info) {
 
413
                ccfsource->info = weather_info_new (ccfsource->location, &prefs, parse_done, source);
 
414
                weather_location_free (ccfsource->location);
 
415
                ccfsource->location = NULL;
 
416
        } else {
 
417
                ccfsource->info = weather_info_update (ccfsource->info, &prefs, parse_done, source);
 
418
        }
 
419
}
 
420
 
 
421
static void
 
422
e_weather_source_ccf_class_init (EWeatherSourceCCFClass *class)
 
423
{
 
424
        EWeatherSourceClass *source_class;
 
425
 
 
426
        source_class = E_WEATHER_SOURCE_CLASS (class);
 
427
        source_class->parse = e_weather_source_ccf_parse;
 
428
}
 
429
 
 
430
static void
 
431
e_weather_source_ccf_init (EWeatherSourceCCF *source)
 
432
{
 
433
        source->location = NULL;
 
434
        source->info = NULL;
 
435
}
 
436
 
 
437
EWeatherSource *
 
438
e_weather_source_ccf_new (const gchar *location)
 
439
{
 
440
        /* Old location is formatted as ccf/AAA[/BBB] - AAA is the 3-letter
 
441
         * station code for identifying the providing station (subdirectory
 
442
         * within the crh data repository). BBB is an optional additional
 
443
         * station ID for the station within the CCF file. If not present,
 
444
         * BBB is assumed to be the same station as AAA.  But the new
 
445
         * location is code/name, where code is 4-letter code.  So if we
 
446
         * got the old format, then migrate to the new one, if possible.
 
447
         */
 
448
 
 
449
        WeatherLocation *wl;
 
450
        EWeatherSourceCCF *source;
 
451
 
 
452
        if (location == NULL)
 
453
                return NULL;
 
454
 
 
455
        if (strncmp (location, "ccf/", 4) == 0)
 
456
                wl = find_location (location + 4, TRUE);
 
457
        else
 
458
                wl = find_location (location, FALSE);
 
459
 
 
460
        if (wl == NULL)
 
461
                return NULL;
 
462
 
 
463
        source = g_object_new (E_TYPE_WEATHER_SOURCE_CCF, NULL);
 
464
        source->location = wl;
 
465
        source->info = NULL;
 
466
 
 
467
        return E_WEATHER_SOURCE (source);
 
468
}